001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase;
019
020import static org.junit.Assert.assertArrayEquals;
021import static org.junit.Assert.assertEquals;
022import static org.junit.Assert.assertNotEquals;
023import static org.junit.Assert.assertNotNull;
024import static org.junit.Assert.assertNull;
025import static org.junit.Assert.assertTrue;
026import static org.mockito.ArgumentMatchers.anyObject;
027import static org.mockito.Mockito.doReturn;
028import static org.mockito.Mockito.mock;
029import static org.mockito.Mockito.reset;
030import static org.mockito.Mockito.times;
031import static org.mockito.Mockito.verify;
032
033import java.io.IOException;
034import java.util.ArrayList;
035import java.util.Collections;
036import java.util.HashMap;
037import java.util.List;
038import java.util.Map;
039import java.util.Random;
040import java.util.concurrent.ThreadLocalRandom;
041import java.util.concurrent.TimeUnit;
042import org.apache.hadoop.conf.Configuration;
043import org.apache.hadoop.hbase.client.Admin;
044import org.apache.hadoop.hbase.client.Connection;
045import org.apache.hadoop.hbase.client.ConnectionFactory;
046import org.apache.hadoop.hbase.client.Get;
047import org.apache.hadoop.hbase.client.Put;
048import org.apache.hadoop.hbase.client.RegionInfo;
049import org.apache.hadoop.hbase.client.RegionInfoBuilder;
050import org.apache.hadoop.hbase.client.RegionLocator;
051import org.apache.hadoop.hbase.client.Result;
052import org.apache.hadoop.hbase.client.Table;
053import org.apache.hadoop.hbase.ipc.CallRunner;
054import org.apache.hadoop.hbase.ipc.DelegatingRpcScheduler;
055import org.apache.hadoop.hbase.ipc.PriorityFunction;
056import org.apache.hadoop.hbase.ipc.RpcScheduler;
057import org.apache.hadoop.hbase.master.HMaster;
058import org.apache.hadoop.hbase.regionserver.HRegion;
059import org.apache.hadoop.hbase.regionserver.HRegionServer;
060import org.apache.hadoop.hbase.regionserver.RSRpcServices;
061import org.apache.hadoop.hbase.regionserver.SimpleRpcSchedulerFactory;
062import org.apache.hadoop.hbase.testclassification.MediumTests;
063import org.apache.hadoop.hbase.testclassification.MiscTests;
064import org.apache.hadoop.hbase.util.Bytes;
065import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
066import org.apache.hadoop.hbase.util.ManualEnvironmentEdge;
067import org.apache.hadoop.hbase.util.Pair;
068import org.junit.AfterClass;
069import org.junit.BeforeClass;
070import org.junit.ClassRule;
071import org.junit.Rule;
072import org.junit.Test;
073import org.junit.experimental.categories.Category;
074import org.junit.rules.TestName;
075import org.slf4j.Logger;
076import org.slf4j.LoggerFactory;
077
078import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
079
080/**
081 * Test {@link org.apache.hadoop.hbase.MetaTableAccessor}.
082 */
083@Category({ MiscTests.class, MediumTests.class })
084@SuppressWarnings("deprecation")
085public class TestMetaTableAccessor {
086  @ClassRule
087  public static final HBaseClassTestRule CLASS_RULE =
088    HBaseClassTestRule.forClass(TestMetaTableAccessor.class);
089
090  private static final Logger LOG = LoggerFactory.getLogger(TestMetaTableAccessor.class);
091  private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
092  private static Connection connection;
093
094  @Rule
095  public TestName name = new TestName();
096
097  @BeforeClass
098  public static void beforeClass() throws Exception {
099    UTIL.startMiniCluster(3);
100
101    Configuration c = new Configuration(UTIL.getConfiguration());
102    // Tests to 4 retries every 5 seconds. Make it try every 1 second so more
103    // responsive. 1 second is default as is ten retries.
104    c.setLong("hbase.client.pause", 1000);
105    c.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 10);
106    connection = ConnectionFactory.createConnection(c);
107  }
108
109  @AfterClass
110  public static void afterClass() throws Exception {
111    connection.close();
112    UTIL.shutdownMiniCluster();
113  }
114
115  /**
116   * Test for HBASE-23044.
117   */
118  @Test
119  public void testGetMergeRegions() throws Exception {
120    TableName tn = TableName.valueOf(this.name.getMethodName());
121    UTIL.createMultiRegionTable(tn, Bytes.toBytes("CF"), 4);
122    UTIL.waitTableAvailable(tn);
123    try (Admin admin = UTIL.getAdmin()) {
124      List<RegionInfo> regions = admin.getRegions(tn);
125      assertEquals(4, regions.size());
126      admin.mergeRegionsAsync(regions.get(0).getRegionName(), regions.get(1).getRegionName(), false)
127        .get(60, TimeUnit.SECONDS);
128      admin.mergeRegionsAsync(regions.get(2).getRegionName(), regions.get(3).getRegionName(), false)
129        .get(60, TimeUnit.SECONDS);
130
131      List<RegionInfo> mergedRegions = admin.getRegions(tn);
132      assertEquals(2, mergedRegions.size());
133      RegionInfo mergedRegion0 = mergedRegions.get(0);
134      RegionInfo mergedRegion1 = mergedRegions.get(1);
135
136      List<RegionInfo> mergeParents =
137        MetaTableAccessor.getMergeRegions(connection, mergedRegion0.getRegionName());
138      assertTrue(mergeParents.contains(regions.get(0)));
139      assertTrue(mergeParents.contains(regions.get(1)));
140      mergeParents = MetaTableAccessor.getMergeRegions(connection, mergedRegion1.getRegionName());
141      assertTrue(mergeParents.contains(regions.get(2)));
142      assertTrue(mergeParents.contains(regions.get(3)));
143
144      // Delete merge qualifiers for mergedRegion0, then cannot getMergeRegions again
145      MetaTableAccessor.deleteMergeQualifiers(connection, mergedRegion0);
146      mergeParents = MetaTableAccessor.getMergeRegions(connection, mergedRegion0.getRegionName());
147      assertNull(mergeParents);
148
149      mergeParents = MetaTableAccessor.getMergeRegions(connection, mergedRegion1.getRegionName());
150      assertTrue(mergeParents.contains(regions.get(2)));
151      assertTrue(mergeParents.contains(regions.get(3)));
152    }
153    UTIL.deleteTable(tn);
154  }
155
156  @Test
157  public void testAddMergeRegions() throws IOException {
158    TableName tn = TableName.valueOf(this.name.getMethodName());
159    Put put = new Put(Bytes.toBytes(this.name.getMethodName()));
160    List<RegionInfo> ris = new ArrayList<>();
161    int limit = 10;
162    byte[] previous = HConstants.EMPTY_START_ROW;
163    for (int i = 0; i < limit; i++) {
164      RegionInfo ri =
165        RegionInfoBuilder.newBuilder(tn).setStartKey(previous).setEndKey(Bytes.toBytes(i)).build();
166      ris.add(ri);
167    }
168    put = MetaTableAccessor.addMergeRegions(put, ris);
169    List<Cell> cells = put.getFamilyCellMap().get(HConstants.CATALOG_FAMILY);
170    String previousQualifier = null;
171    assertEquals(limit, cells.size());
172    for (Cell cell : cells) {
173      LOG.info(cell.toString());
174      String qualifier = Bytes.toString(cell.getQualifierArray());
175      assertTrue(qualifier.startsWith(HConstants.MERGE_QUALIFIER_PREFIX_STR));
176      assertNotEquals(qualifier, previousQualifier);
177      previousQualifier = qualifier;
178    }
179  }
180
181  @Test
182  public void testIsMetaWhenAllHealthy() throws InterruptedException {
183    HMaster m = UTIL.getMiniHBaseCluster().getMaster();
184    assertTrue(m.waitForMetaOnline());
185  }
186
187  @Test
188  public void testIsMetaWhenMetaGoesOffline() throws InterruptedException {
189    HMaster m = UTIL.getMiniHBaseCluster().getMaster();
190    int index = UTIL.getMiniHBaseCluster().getServerWithMeta();
191    HRegionServer rsWithMeta = UTIL.getMiniHBaseCluster().getRegionServer(index);
192    rsWithMeta.abort("TESTING");
193    assertTrue(m.waitForMetaOnline());
194  }
195
196  /**
197   * Does {@link MetaTableAccessor#getRegion(Connection, byte[])} and a write against hbase:meta
198   * while its hosted server is restarted to prove our retrying works.
199   */
200  @Test
201  public void testRetrying() throws IOException, InterruptedException {
202    final TableName tableName = TableName.valueOf(name.getMethodName());
203    LOG.info("Started " + tableName);
204    Table t = UTIL.createMultiRegionTable(tableName, HConstants.CATALOG_FAMILY);
205    int regionCount = -1;
206    try (RegionLocator r = UTIL.getConnection().getRegionLocator(tableName)) {
207      regionCount = r.getStartKeys().length;
208    }
209    // Test it works getting a region from just made user table.
210    final List<RegionInfo> regions = testGettingTableRegions(connection, tableName, regionCount);
211    MetaTask reader = new MetaTask(connection, "reader") {
212      @Override
213      void metaTask() throws Throwable {
214        testGetRegion(connection, regions.get(0));
215        LOG.info("Read " + regions.get(0).getEncodedName());
216      }
217    };
218    MetaTask writer = new MetaTask(connection, "writer") {
219
220      @Override
221      void metaTask() throws IOException {
222        MetaTableAccessor.addRegionsToMeta(connection, Collections.singletonList(regions.get(0)),
223          1);
224        LOG.info("Wrote " + regions.get(0).getEncodedName());
225      }
226    };
227    reader.start();
228    writer.start();
229
230    // We're gonna check how it takes. If it takes too long, we will consider
231    // it as a fail. We can't put that in the @Test tag as we want to close
232    // the threads nicely
233    final long timeOut = 180000;
234    long startTime = EnvironmentEdgeManager.currentTime();
235
236    try {
237      // Make sure reader and writer are working.
238      assertTrue(reader.isProgressing());
239      assertTrue(writer.isProgressing());
240
241      // Kill server hosting meta -- twice . See if our reader/writer ride over the
242      // meta moves. They'll need to retry.
243      for (int i = 0; i < 2; i++) {
244        LOG.info("Restart=" + i);
245        UTIL.ensureSomeRegionServersAvailable(2);
246        int index = -1;
247        do {
248          index = UTIL.getMiniHBaseCluster().getServerWithMeta();
249        } while (index == -1 && startTime + timeOut < EnvironmentEdgeManager.currentTime());
250
251        if (index != -1) {
252          UTIL.getMiniHBaseCluster().abortRegionServer(index);
253          UTIL.getMiniHBaseCluster().waitOnRegionServer(index);
254        }
255      }
256
257      assertTrue("reader: " + reader.toString(), reader.isProgressing());
258      assertTrue("writer: " + writer.toString(), writer.isProgressing());
259    } catch (IOException e) {
260      throw e;
261    } finally {
262      reader.stop = true;
263      writer.stop = true;
264      reader.join();
265      writer.join();
266      t.close();
267    }
268    long exeTime = EnvironmentEdgeManager.currentTime() - startTime;
269    assertTrue("Timeout: test took " + exeTime / 1000 + " sec", exeTime < timeOut);
270  }
271
272  /**
273   * Thread that runs a MetaTableAccessor task until asked stop.
274   */
275  abstract static class MetaTask extends Thread {
276    boolean stop = false;
277    int count = 0;
278    Throwable t = null;
279    final Connection connection;
280
281    MetaTask(final Connection connection, final String name) {
282      super(name);
283      this.connection = connection;
284    }
285
286    @Override
287    public void run() {
288      try {
289        while (!this.stop) {
290          LOG.info("Before " + this.getName() + ", count=" + this.count);
291          metaTask();
292          this.count += 1;
293          LOG.info("After " + this.getName() + ", count=" + this.count);
294          Thread.sleep(100);
295        }
296      } catch (Throwable t) {
297        LOG.info(this.getName() + " failed", t);
298        this.t = t;
299      }
300    }
301
302    boolean isProgressing() throws InterruptedException {
303      int currentCount = this.count;
304      while (currentCount == this.count) {
305        if (!isAlive()) return false;
306        if (this.t != null) return false;
307        Thread.sleep(10);
308      }
309      return true;
310    }
311
312    @Override
313    public String toString() {
314      return "count=" + this.count + ", t=" + (this.t == null ? "null" : this.t.toString());
315    }
316
317    abstract void metaTask() throws Throwable;
318  }
319
320  @Test
321  public void testGetRegion() throws IOException, InterruptedException {
322    final String name = this.name.getMethodName();
323    LOG.info("Started " + name);
324    // Test get on non-existent region.
325    Pair<RegionInfo, ServerName> pair =
326      MetaTableAccessor.getRegion(connection, Bytes.toBytes("nonexistent-region"));
327    assertNull(pair);
328    LOG.info("Finished " + name);
329  }
330
331  // Test for the optimization made in HBASE-3650
332  @Test
333  public void testScanMetaForTable() throws IOException, InterruptedException {
334    final TableName tableName = TableName.valueOf(name.getMethodName());
335    LOG.info("Started " + tableName);
336
337    /**
338     * Create 2 tables - testScanMetaForTable - testScanMetaForTablf
339     **/
340
341    UTIL.createTable(tableName, HConstants.CATALOG_FAMILY);
342    // name that is +1 greater than the first one (e+1=f)
343    TableName greaterName = TableName.valueOf("testScanMetaForTablf");
344    UTIL.createTable(greaterName, HConstants.CATALOG_FAMILY);
345
346    // Now make sure we only get the regions from 1 of the tables at a time
347
348    assertEquals(1, MetaTableAccessor.getTableRegions(connection, tableName).size());
349    assertEquals(1, MetaTableAccessor.getTableRegions(connection, greaterName).size());
350  }
351
352  private static List<RegionInfo> testGettingTableRegions(final Connection connection,
353    final TableName name, final int regionCount) throws IOException, InterruptedException {
354    List<RegionInfo> regions = MetaTableAccessor.getTableRegions(connection, name);
355    assertEquals(regionCount, regions.size());
356    Pair<RegionInfo, ServerName> pair =
357      MetaTableAccessor.getRegion(connection, regions.get(0).getRegionName());
358    assertEquals(regions.get(0).getEncodedName(), pair.getFirst().getEncodedName());
359    return regions;
360  }
361
362  private static void testGetRegion(final Connection connection, final RegionInfo region)
363    throws IOException, InterruptedException {
364    Pair<RegionInfo, ServerName> pair =
365      MetaTableAccessor.getRegion(connection, region.getRegionName());
366    assertEquals(region.getEncodedName(), pair.getFirst().getEncodedName());
367  }
368
369  @Test
370  public void testParseReplicaIdFromServerColumn() {
371    String column1 = HConstants.SERVER_QUALIFIER_STR;
372    assertEquals(0, MetaTableAccessor.parseReplicaIdFromServerColumn(Bytes.toBytes(column1)));
373    String column2 = column1 + MetaTableAccessor.META_REPLICA_ID_DELIMITER;
374    assertEquals(-1, MetaTableAccessor.parseReplicaIdFromServerColumn(Bytes.toBytes(column2)));
375    String column3 = column2 + "00";
376    assertEquals(-1, MetaTableAccessor.parseReplicaIdFromServerColumn(Bytes.toBytes(column3)));
377    String column4 = column3 + "2A";
378    assertEquals(42, MetaTableAccessor.parseReplicaIdFromServerColumn(Bytes.toBytes(column4)));
379    String column5 = column4 + "2A";
380    assertEquals(-1, MetaTableAccessor.parseReplicaIdFromServerColumn(Bytes.toBytes(column5)));
381    String column6 = HConstants.STARTCODE_QUALIFIER_STR;
382    assertEquals(-1, MetaTableAccessor.parseReplicaIdFromServerColumn(Bytes.toBytes(column6)));
383  }
384
385  /**
386   * The info we can get from the regionName is: table name, start key, regionId, replicaId.
387   */
388  @Test
389  public void testParseRegionInfoFromRegionName() throws IOException {
390    RegionInfo originalRegionInfo =
391      RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())).setRegionId(999999L)
392        .setStartKey(Bytes.toBytes("2")).setEndKey(Bytes.toBytes("3")).setReplicaId(1).build();
393    RegionInfo newParsedRegionInfo =
394      MetaTableAccessor.parseRegionInfoFromRegionName(originalRegionInfo.getRegionName());
395    assertEquals("Parse TableName error", originalRegionInfo.getTable(),
396      newParsedRegionInfo.getTable());
397    assertEquals("Parse regionId error", originalRegionInfo.getRegionId(),
398      newParsedRegionInfo.getRegionId());
399    assertTrue("Parse startKey error",
400      Bytes.equals(originalRegionInfo.getStartKey(), newParsedRegionInfo.getStartKey()));
401    assertEquals("Parse replicaId error", originalRegionInfo.getReplicaId(),
402      newParsedRegionInfo.getReplicaId());
403    assertTrue("We can't parse endKey from regionName only",
404      Bytes.equals(HConstants.EMPTY_END_ROW, newParsedRegionInfo.getEndKey()));
405  }
406
407  @Test
408  public void testMetaReaderGetColumnMethods() {
409    assertArrayEquals(HConstants.SERVER_QUALIFIER, MetaTableAccessor.getServerColumn(0));
410    assertArrayEquals(
411      Bytes.toBytes(
412        HConstants.SERVER_QUALIFIER_STR + MetaTableAccessor.META_REPLICA_ID_DELIMITER + "002A"),
413      MetaTableAccessor.getServerColumn(42));
414
415    assertArrayEquals(HConstants.STARTCODE_QUALIFIER, MetaTableAccessor.getStartCodeColumn(0));
416    assertArrayEquals(
417      Bytes.toBytes(
418        HConstants.STARTCODE_QUALIFIER_STR + MetaTableAccessor.META_REPLICA_ID_DELIMITER + "002A"),
419      MetaTableAccessor.getStartCodeColumn(42));
420
421    assertArrayEquals(HConstants.SEQNUM_QUALIFIER, MetaTableAccessor.getSeqNumColumn(0));
422    assertArrayEquals(
423      Bytes.toBytes(
424        HConstants.SEQNUM_QUALIFIER_STR + MetaTableAccessor.META_REPLICA_ID_DELIMITER + "002A"),
425      MetaTableAccessor.getSeqNumColumn(42));
426  }
427
428  @Test
429  public void testMetaLocationsForRegionReplicas() throws IOException {
430    Random rand = ThreadLocalRandom.current();
431
432    ServerName serverName0 = ServerName.valueOf("foo", 60010, rand.nextLong());
433    ServerName serverName1 = ServerName.valueOf("bar", 60010, rand.nextLong());
434    ServerName serverName100 = ServerName.valueOf("baz", 60010, rand.nextLong());
435
436    long regionId = EnvironmentEdgeManager.currentTime();
437    RegionInfo primary = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
438      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
439      .setRegionId(regionId).setReplicaId(0).build();
440    RegionInfo replica1 = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
441      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
442      .setRegionId(regionId).setReplicaId(1).build();
443    RegionInfo replica100 = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
444      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
445      .setRegionId(regionId).setReplicaId(100).build();
446
447    long seqNum0 = rand.nextLong();
448    long seqNum1 = rand.nextLong();
449    long seqNum100 = rand.nextLong();
450
451    try (Table meta = MetaTableAccessor.getMetaHTable(connection)) {
452      MetaTableAccessor.updateRegionLocation(connection, primary, serverName0, seqNum0,
453        EnvironmentEdgeManager.currentTime());
454
455      // assert that the server, startcode and seqNum columns are there for the primary region
456      assertMetaLocation(meta, primary.getRegionName(), serverName0, seqNum0, 0, true);
457
458      // add replica = 1
459      MetaTableAccessor.updateRegionLocation(connection, replica1, serverName1, seqNum1,
460        EnvironmentEdgeManager.currentTime());
461      // check whether the primary is still there
462      assertMetaLocation(meta, primary.getRegionName(), serverName0, seqNum0, 0, true);
463      // now check for replica 1
464      assertMetaLocation(meta, primary.getRegionName(), serverName1, seqNum1, 1, true);
465
466      // add replica = 1
467      MetaTableAccessor.updateRegionLocation(connection, replica100, serverName100, seqNum100,
468        EnvironmentEdgeManager.currentTime());
469      // check whether the primary is still there
470      assertMetaLocation(meta, primary.getRegionName(), serverName0, seqNum0, 0, true);
471      // check whether the replica 1 is still there
472      assertMetaLocation(meta, primary.getRegionName(), serverName1, seqNum1, 1, true);
473      // now check for replica 1
474      assertMetaLocation(meta, primary.getRegionName(), serverName100, seqNum100, 100, true);
475    }
476  }
477
478  public static void assertMetaLocation(Table meta, byte[] row, ServerName serverName, long seqNum,
479    int replicaId, boolean checkSeqNum) throws IOException {
480    Get get = new Get(row);
481    Result result = meta.get(get);
482    assertTrue(Bytes.equals(
483      result.getValue(HConstants.CATALOG_FAMILY, MetaTableAccessor.getServerColumn(replicaId)),
484      Bytes.toBytes(serverName.getAddress().toString())));
485    assertTrue(Bytes.equals(
486      result.getValue(HConstants.CATALOG_FAMILY, MetaTableAccessor.getStartCodeColumn(replicaId)),
487      Bytes.toBytes(serverName.getStartcode())));
488    if (checkSeqNum) {
489      assertTrue(Bytes.equals(
490        result.getValue(HConstants.CATALOG_FAMILY, MetaTableAccessor.getSeqNumColumn(replicaId)),
491        Bytes.toBytes(seqNum)));
492    }
493  }
494
495  public static void assertEmptyMetaLocation(Table meta, byte[] row, int replicaId)
496    throws IOException {
497    Get get = new Get(row);
498    Result result = meta.get(get);
499    Cell serverCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
500      MetaTableAccessor.getServerColumn(replicaId));
501    Cell startCodeCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
502      MetaTableAccessor.getStartCodeColumn(replicaId));
503    assertNotNull(serverCell);
504    assertNotNull(startCodeCell);
505    assertEquals(0, serverCell.getValueLength());
506    assertEquals(0, startCodeCell.getValueLength());
507  }
508
509  @Test
510  public void testMetaLocationForRegionReplicasIsAddedAtTableCreation() throws IOException {
511    long regionId = EnvironmentEdgeManager.currentTime();
512    RegionInfo primary = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
513      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
514      .setRegionId(regionId).setReplicaId(0).build();
515
516    Table meta = MetaTableAccessor.getMetaHTable(connection);
517    try {
518      List<RegionInfo> regionInfos = Lists.newArrayList(primary);
519      MetaTableAccessor.addRegionsToMeta(connection, regionInfos, 3);
520
521      assertEmptyMetaLocation(meta, primary.getRegionName(), 1);
522      assertEmptyMetaLocation(meta, primary.getRegionName(), 2);
523    } finally {
524      meta.close();
525    }
526  }
527
528  @Test
529  public void testMetaLocationForRegionReplicasIsAddedAtRegionSplit() throws IOException {
530    long regionId = EnvironmentEdgeManager.currentTime();
531    ServerName serverName0 =
532      ServerName.valueOf("foo", 60010, ThreadLocalRandom.current().nextLong());
533    RegionInfo parent = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
534      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
535      .setRegionId(regionId).setReplicaId(0).build();
536
537    RegionInfo splitA = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
538      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(Bytes.toBytes("a")).setSplit(false)
539      .setRegionId(regionId + 1).setReplicaId(0).build();
540    RegionInfo splitB = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
541      .setStartKey(Bytes.toBytes("a")).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
542      .setRegionId(regionId + 1).setReplicaId(0).build();
543
544    try (Table meta = MetaTableAccessor.getMetaHTable(connection)) {
545      List<RegionInfo> regionInfos = Lists.newArrayList(parent);
546      MetaTableAccessor.addRegionsToMeta(connection, regionInfos, 3);
547
548      MetaTableAccessor.splitRegion(connection, parent, -1L, splitA, splitB, serverName0, 3);
549
550      assertEmptyMetaLocation(meta, splitA.getRegionName(), 1);
551      assertEmptyMetaLocation(meta, splitA.getRegionName(), 2);
552      assertEmptyMetaLocation(meta, splitB.getRegionName(), 1);
553      assertEmptyMetaLocation(meta, splitB.getRegionName(), 2);
554    }
555  }
556
557  @Test
558  public void testMetaLocationForRegionReplicasIsAddedAtRegionMerge() throws IOException {
559    long regionId = EnvironmentEdgeManager.currentTime();
560    ServerName serverName0 =
561      ServerName.valueOf("foo", 60010, ThreadLocalRandom.current().nextLong());
562
563    RegionInfo parentA = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
564      .setStartKey(Bytes.toBytes("a")).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
565      .setRegionId(regionId).setReplicaId(0).build();
566
567    RegionInfo parentB = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
568      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(Bytes.toBytes("a")).setSplit(false)
569      .setRegionId(regionId).setReplicaId(0).build();
570    RegionInfo merged = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
571      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
572      .setRegionId(regionId + 1).setReplicaId(0).build();
573
574    try (Table meta = MetaTableAccessor.getMetaHTable(connection)) {
575      List<RegionInfo> regionInfos = Lists.newArrayList(parentA, parentB);
576      MetaTableAccessor.addRegionsToMeta(connection, regionInfos, 3);
577      MetaTableAccessor.mergeRegions(connection, merged, getMapOfRegionsToSeqNum(parentA, parentB),
578        serverName0, 3);
579      assertEmptyMetaLocation(meta, merged.getRegionName(), 1);
580      assertEmptyMetaLocation(meta, merged.getRegionName(), 2);
581    }
582  }
583
584  private Map<RegionInfo, Long> getMapOfRegionsToSeqNum(RegionInfo... regions) {
585    Map<RegionInfo, Long> mids = new HashMap<>(regions.length);
586    for (RegionInfo region : regions) {
587      mids.put(region, -1L);
588    }
589    return mids;
590  }
591
592  @Test
593  public void testMetaScanner() throws Exception {
594    LOG.info("Starting " + name.getMethodName());
595
596    final TableName tableName = TableName.valueOf(name.getMethodName());
597    final byte[] FAMILY = Bytes.toBytes("family");
598    final byte[][] SPLIT_KEYS =
599      new byte[][] { Bytes.toBytes("region_a"), Bytes.toBytes("region_b") };
600
601    UTIL.createTable(tableName, FAMILY, SPLIT_KEYS);
602    Table table = connection.getTable(tableName);
603    // Make sure all the regions are deployed
604    UTIL.countRows(table);
605
606    MetaTableAccessor.Visitor visitor = mock(MetaTableAccessor.Visitor.class);
607    doReturn(true).when(visitor).visit((Result) anyObject());
608
609    // Scanning the entire table should give us three rows
610    MetaTableAccessor.scanMetaForTableRegions(connection, visitor, tableName);
611    verify(visitor, times(3)).visit((Result) anyObject());
612
613    // Scanning the table with a specified empty start row should also
614    // give us three hbase:meta rows
615    reset(visitor);
616    doReturn(true).when(visitor).visit((Result) anyObject());
617    MetaTableAccessor.scanMeta(connection, visitor, tableName, null, 1000);
618    verify(visitor, times(3)).visit((Result) anyObject());
619
620    // Scanning the table starting in the middle should give us two rows:
621    // region_a and region_b
622    reset(visitor);
623    doReturn(true).when(visitor).visit((Result) anyObject());
624    MetaTableAccessor.scanMeta(connection, visitor, tableName, Bytes.toBytes("region_ac"), 1000);
625    verify(visitor, times(2)).visit((Result) anyObject());
626
627    // Scanning with a limit of 1 should only give us one row
628    reset(visitor);
629    doReturn(true).when(visitor).visit((Result) anyObject());
630    MetaTableAccessor.scanMeta(connection, visitor, tableName, Bytes.toBytes("region_ac"), 1);
631    verify(visitor, times(1)).visit((Result) anyObject());
632    table.close();
633  }
634
635  /**
636   * Tests whether maximum of masters system time versus RSs local system time is used
637   */
638  @Test
639  public void testMastersSystemTimeIsUsedInUpdateLocations() throws IOException {
640    long regionId = EnvironmentEdgeManager.currentTime();
641    RegionInfo regionInfo = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
642      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
643      .setRegionId(regionId).setReplicaId(0).build();
644
645    ServerName sn = ServerName.valueOf("bar", 0, 0);
646    try (Table meta = MetaTableAccessor.getMetaHTable(connection)) {
647      List<RegionInfo> regionInfos = Lists.newArrayList(regionInfo);
648      MetaTableAccessor.addRegionsToMeta(connection, regionInfos, 1);
649
650      long masterSystemTime = EnvironmentEdgeManager.currentTime() + 123456789;
651      MetaTableAccessor.updateRegionLocation(connection, regionInfo, sn, 1, masterSystemTime);
652
653      Get get = new Get(regionInfo.getRegionName());
654      Result result = meta.get(get);
655      Cell serverCell =
656        result.getColumnLatestCell(HConstants.CATALOG_FAMILY, MetaTableAccessor.getServerColumn(0));
657      Cell startCodeCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
658        MetaTableAccessor.getStartCodeColumn(0));
659      Cell seqNumCell =
660        result.getColumnLatestCell(HConstants.CATALOG_FAMILY, MetaTableAccessor.getSeqNumColumn(0));
661      assertNotNull(serverCell);
662      assertNotNull(startCodeCell);
663      assertNotNull(seqNumCell);
664      assertTrue(serverCell.getValueLength() > 0);
665      assertTrue(startCodeCell.getValueLength() > 0);
666      assertTrue(seqNumCell.getValueLength() > 0);
667      assertEquals(masterSystemTime, serverCell.getTimestamp());
668      assertEquals(masterSystemTime, startCodeCell.getTimestamp());
669      assertEquals(masterSystemTime, seqNumCell.getTimestamp());
670    }
671  }
672
673  @Test
674  public void testMastersSystemTimeIsUsedInMergeRegions() throws IOException {
675    long regionId = EnvironmentEdgeManager.currentTime();
676
677    RegionInfo regionInfoA = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
678      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(new byte[] { 'a' }).setSplit(false)
679      .setRegionId(regionId).setReplicaId(0).build();
680
681    RegionInfo regionInfoB = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
682      .setStartKey(new byte[] { 'a' }).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
683      .setRegionId(regionId).setReplicaId(0).build();
684    RegionInfo mergedRegionInfo =
685      RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
686        .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
687        .setRegionId(regionId).setReplicaId(0).build();
688
689    ServerName sn = ServerName.valueOf("bar", 0, 0);
690    try (Table meta = MetaTableAccessor.getMetaHTable(connection)) {
691      List<RegionInfo> regionInfos = Lists.newArrayList(regionInfoA, regionInfoB);
692      MetaTableAccessor.addRegionsToMeta(connection, regionInfos, 1);
693
694      // write the serverName column with a big current time, but set the masters time as even
695      // bigger. When region merge deletes the rows for regionA and regionB, the serverName columns
696      // should not be seen by the following get
697      long serverNameTime = EnvironmentEdgeManager.currentTime() + 100000000;
698      long masterSystemTime = EnvironmentEdgeManager.currentTime() + 123456789;
699
700      // write the serverName columns
701      MetaTableAccessor.updateRegionLocation(connection, regionInfoA, sn, 1, serverNameTime);
702
703      // assert that we have the serverName column with expected ts
704      Get get = new Get(mergedRegionInfo.getRegionName());
705      Result result = meta.get(get);
706      Cell serverCell =
707        result.getColumnLatestCell(HConstants.CATALOG_FAMILY, MetaTableAccessor.getServerColumn(0));
708      assertNotNull(serverCell);
709      assertEquals(serverNameTime, serverCell.getTimestamp());
710
711      ManualEnvironmentEdge edge = new ManualEnvironmentEdge();
712      edge.setValue(masterSystemTime);
713      EnvironmentEdgeManager.injectEdge(edge);
714      try {
715        // now merge the regions, effectively deleting the rows for region a and b.
716        MetaTableAccessor.mergeRegions(connection, mergedRegionInfo,
717          getMapOfRegionsToSeqNum(regionInfoA, regionInfoB), sn, 1);
718      } finally {
719        EnvironmentEdgeManager.reset();
720      }
721
722      result = meta.get(get);
723      serverCell =
724        result.getColumnLatestCell(HConstants.CATALOG_FAMILY, MetaTableAccessor.getServerColumn(0));
725      Cell startCodeCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
726        MetaTableAccessor.getStartCodeColumn(0));
727      Cell seqNumCell =
728        result.getColumnLatestCell(HConstants.CATALOG_FAMILY, MetaTableAccessor.getSeqNumColumn(0));
729      assertNull(serverCell);
730      assertNull(startCodeCell);
731      assertNull(seqNumCell);
732    }
733  }
734
735  public static class SpyingRpcSchedulerFactory extends SimpleRpcSchedulerFactory {
736    @Override
737    public RpcScheduler create(Configuration conf, PriorityFunction priority, Abortable server) {
738      final RpcScheduler delegate = super.create(conf, priority, server);
739      return new SpyingRpcScheduler(delegate);
740    }
741  }
742
743  public static class SpyingRpcScheduler extends DelegatingRpcScheduler {
744    long numPriorityCalls = 0;
745
746    public SpyingRpcScheduler(RpcScheduler delegate) {
747      super(delegate);
748    }
749
750    @Override
751    public boolean dispatch(CallRunner task) {
752      int priority = task.getRpcCall().getPriority();
753
754      if (priority > HConstants.QOS_THRESHOLD) {
755        numPriorityCalls++;
756      }
757      return super.dispatch(task);
758    }
759  }
760
761  @Test
762  public void testMetaUpdatesGoToPriorityQueue() throws Exception {
763    // This test has to be end-to-end, and do the verification from the server side
764    Configuration c = UTIL.getConfiguration();
765
766    c.set(RSRpcServices.REGION_SERVER_RPC_SCHEDULER_FACTORY_CLASS,
767      SpyingRpcSchedulerFactory.class.getName());
768
769    // restart so that new config takes place
770    afterClass();
771    beforeClass();
772
773    final TableName tableName = TableName.valueOf(name.getMethodName());
774    try (Admin admin = connection.getAdmin();
775      RegionLocator rl = connection.getRegionLocator(tableName)) {
776
777      // create a table and prepare for a manual split
778      UTIL.createTable(tableName, "cf1");
779
780      HRegionLocation loc = rl.getAllRegionLocations().get(0);
781      RegionInfo parent = loc.getRegionInfo();
782      long rid = 1000;
783      byte[] splitKey = Bytes.toBytes("a");
784      RegionInfo splitA =
785        RegionInfoBuilder.newBuilder(parent.getTable()).setStartKey(parent.getStartKey())
786          .setEndKey(splitKey).setSplit(false).setRegionId(rid).build();
787      RegionInfo splitB = RegionInfoBuilder.newBuilder(parent.getTable()).setStartKey(splitKey)
788        .setEndKey(parent.getEndKey()).setSplit(false).setRegionId(rid).build();
789
790      // find the meta server
791      MiniHBaseCluster cluster = UTIL.getMiniHBaseCluster();
792      int rsIndex = cluster.getServerWithMeta();
793      HRegionServer rs;
794      if (rsIndex >= 0) {
795        rs = cluster.getRegionServer(rsIndex);
796      } else {
797        // it is in master
798        rs = cluster.getMaster();
799      }
800      SpyingRpcScheduler scheduler = (SpyingRpcScheduler) rs.getRpcServer().getScheduler();
801      long prevCalls = scheduler.numPriorityCalls;
802      MetaTableAccessor.splitRegion(connection, parent, -1L, splitA, splitB, loc.getServerName(),
803        1);
804
805      assertTrue(prevCalls < scheduler.numPriorityCalls);
806    }
807  }
808
809  @Test
810  public void testEmptyMetaDaughterLocationDuringSplit() throws IOException {
811    long regionId = EnvironmentEdgeManager.currentTime();
812    ServerName serverName0 =
813      ServerName.valueOf("foo", 60010, ThreadLocalRandom.current().nextLong());
814    RegionInfo parent = RegionInfoBuilder.newBuilder(TableName.valueOf("table_foo"))
815      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
816      .setRegionId(regionId).setReplicaId(0).build();
817    RegionInfo splitA = RegionInfoBuilder.newBuilder(TableName.valueOf("table_foo"))
818      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(Bytes.toBytes("a")).setSplit(false)
819      .setRegionId(regionId + 1).setReplicaId(0).build();
820    RegionInfo splitB = RegionInfoBuilder.newBuilder(TableName.valueOf("table_foo"))
821      .setStartKey(Bytes.toBytes("a")).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
822      .setRegionId(regionId + 1).setReplicaId(0).build();
823
824    Table meta = MetaTableAccessor.getMetaHTable(connection);
825    try {
826      List<RegionInfo> regionInfos = Lists.newArrayList(parent);
827      MetaTableAccessor.addRegionsToMeta(connection, regionInfos, 3);
828
829      MetaTableAccessor.splitRegion(connection, parent, -1L, splitA, splitB, serverName0, 3);
830      Get get1 = new Get(splitA.getRegionName());
831      Result resultA = meta.get(get1);
832      Cell serverCellA = resultA.getColumnLatestCell(HConstants.CATALOG_FAMILY,
833        MetaTableAccessor.getServerColumn(splitA.getReplicaId()));
834      Cell startCodeCellA = resultA.getColumnLatestCell(HConstants.CATALOG_FAMILY,
835        MetaTableAccessor.getStartCodeColumn(splitA.getReplicaId()));
836      assertNull(serverCellA);
837      assertNull(startCodeCellA);
838
839      Get get2 = new Get(splitA.getRegionName());
840      Result resultB = meta.get(get2);
841      Cell serverCellB = resultB.getColumnLatestCell(HConstants.CATALOG_FAMILY,
842        MetaTableAccessor.getServerColumn(splitB.getReplicaId()));
843      Cell startCodeCellB = resultB.getColumnLatestCell(HConstants.CATALOG_FAMILY,
844        MetaTableAccessor.getStartCodeColumn(splitB.getReplicaId()));
845      assertNull(serverCellB);
846      assertNull(startCodeCellB);
847    } finally {
848      if (meta != null) {
849        meta.close();
850      }
851    }
852  }
853
854  @Test
855  public void testScanByRegionEncodedNameExistingRegion() throws Exception {
856    final TableName tableName = TableName.valueOf("testScanByRegionEncodedNameExistingRegion");
857    UTIL.createTable(tableName, "cf");
858    final List<HRegion> regions = UTIL.getHBaseCluster().getRegions(tableName);
859    final String encodedName = regions.get(0).getRegionInfo().getEncodedName();
860    final Result result =
861      MetaTableAccessor.scanByRegionEncodedName(UTIL.getConnection(), encodedName);
862    assertNotNull(result);
863    assertTrue(result.advance());
864    final String resultingRowKey = CellUtil.getCellKeyAsString(result.current());
865    assertTrue(resultingRowKey.contains(encodedName));
866    UTIL.deleteTable(tableName);
867  }
868
869  @Test
870  public void testScanByRegionEncodedNameNonExistingRegion() throws Exception {
871    final String encodedName = "nonexistingregion";
872    final Result result =
873      MetaTableAccessor.scanByRegionEncodedName(UTIL.getConnection(), encodedName);
874    assertNull(result);
875  }
876}