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