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