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