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