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.waitUntilMetaOnline());
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.waitUntilMetaOnline());
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, 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, parentB, serverName0, 3);
553
554      assertEmptyMetaLocation(meta, merged.getRegionName(), 1);
555      assertEmptyMetaLocation(meta, merged.getRegionName(), 2);
556    }
557  }
558
559  @Test
560  public void testMetaScanner() throws Exception {
561    LOG.info("Starting " + name.getMethodName());
562
563    final TableName tableName = TableName.valueOf(name.getMethodName());
564    final byte[] FAMILY = Bytes.toBytes("family");
565    final byte[][] SPLIT_KEYS =
566        new byte[][] { Bytes.toBytes("region_a"), Bytes.toBytes("region_b") };
567
568    UTIL.createTable(tableName, FAMILY, SPLIT_KEYS);
569    Table table = connection.getTable(tableName);
570    // Make sure all the regions are deployed
571    UTIL.countRows(table);
572
573    MetaTableAccessor.Visitor visitor =
574        mock(MetaTableAccessor.Visitor.class);
575    doReturn(true).when(visitor).visit((Result) anyObject());
576
577    // Scanning the entire table should give us three rows
578    MetaTableAccessor.scanMetaForTableRegions(connection, visitor, tableName);
579    verify(visitor, times(3)).visit((Result) anyObject());
580
581    // Scanning the table with a specified empty start row should also
582    // give us three hbase:meta rows
583    reset(visitor);
584    doReturn(true).when(visitor).visit((Result) anyObject());
585    MetaTableAccessor.scanMeta(connection, visitor, tableName, null, 1000);
586    verify(visitor, times(3)).visit((Result) anyObject());
587
588    // Scanning the table starting in the middle should give us two rows:
589    // region_a and region_b
590    reset(visitor);
591    doReturn(true).when(visitor).visit((Result) anyObject());
592    MetaTableAccessor.scanMeta(connection, visitor, tableName, Bytes.toBytes("region_ac"), 1000);
593    verify(visitor, times(2)).visit((Result) anyObject());
594
595    // Scanning with a limit of 1 should only give us one row
596    reset(visitor);
597    doReturn(true).when(visitor).visit((Result) anyObject());
598    MetaTableAccessor.scanMeta(connection, visitor, tableName, Bytes.toBytes("region_ac"), 1);
599    verify(visitor, times(1)).visit((Result) anyObject());
600    table.close();
601  }
602
603  /**
604   * Tests whether maximum of masters system time versus RSs local system time is used
605   */
606  @Test
607  public void testMastersSystemTimeIsUsedInUpdateLocations() throws IOException {
608    long regionId = System.currentTimeMillis();
609    RegionInfo regionInfo = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
610        .setStartKey(HConstants.EMPTY_START_ROW)
611        .setEndKey(HConstants.EMPTY_END_ROW)
612        .setSplit(false)
613        .setRegionId(regionId)
614        .setReplicaId(0)
615        .build();
616
617    ServerName sn = ServerName.valueOf("bar", 0, 0);
618    try (Table meta = MetaTableAccessor.getMetaHTable(connection)) {
619      List<RegionInfo> regionInfos = Lists.newArrayList(regionInfo);
620      MetaTableAccessor.addRegionsToMeta(connection, regionInfos, 1);
621
622      long masterSystemTime = EnvironmentEdgeManager.currentTime() + 123456789;
623      MetaTableAccessor.updateRegionLocation(connection, regionInfo, sn, 1, masterSystemTime);
624
625      Get get = new Get(regionInfo.getRegionName());
626      Result result = meta.get(get);
627      Cell serverCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
628          MetaTableAccessor.getServerColumn(0));
629      Cell startCodeCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
630        MetaTableAccessor.getStartCodeColumn(0));
631      Cell seqNumCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
632        MetaTableAccessor.getSeqNumColumn(0));
633      assertNotNull(serverCell);
634      assertNotNull(startCodeCell);
635      assertNotNull(seqNumCell);
636      assertTrue(serverCell.getValueLength() > 0);
637      assertTrue(startCodeCell.getValueLength() > 0);
638      assertTrue(seqNumCell.getValueLength() > 0);
639      assertEquals(masterSystemTime, serverCell.getTimestamp());
640      assertEquals(masterSystemTime, startCodeCell.getTimestamp());
641      assertEquals(masterSystemTime, seqNumCell.getTimestamp());
642    }
643  }
644
645  @Test
646  public void testMastersSystemTimeIsUsedInMergeRegions() throws IOException {
647    long regionId = System.currentTimeMillis();
648
649    RegionInfo regionInfoA = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
650        .setStartKey(HConstants.EMPTY_START_ROW)
651        .setEndKey(new byte[] {'a'})
652        .setSplit(false)
653        .setRegionId(regionId)
654        .setReplicaId(0)
655        .build();
656
657    RegionInfo regionInfoB = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
658        .setStartKey(new byte[] {'a'})
659        .setEndKey(HConstants.EMPTY_END_ROW)
660        .setSplit(false)
661        .setRegionId(regionId)
662        .setReplicaId(0)
663        .build();
664    RegionInfo mergedRegionInfo = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
665        .setStartKey(HConstants.EMPTY_START_ROW)
666        .setEndKey(HConstants.EMPTY_END_ROW)
667        .setSplit(false)
668        .setRegionId(regionId)
669        .setReplicaId(0)
670        .build();
671
672    ServerName sn = ServerName.valueOf("bar", 0, 0);
673    try (Table meta = MetaTableAccessor.getMetaHTable(connection)) {
674      List<RegionInfo> regionInfos = Lists.newArrayList(regionInfoA, regionInfoB);
675      MetaTableAccessor.addRegionsToMeta(connection, regionInfos, 1);
676
677      // write the serverName column with a big current time, but set the masters time as even
678      // bigger. When region merge deletes the rows for regionA and regionB, the serverName columns
679      // should not be seen by the following get
680      long serverNameTime = EnvironmentEdgeManager.currentTime()   + 100000000;
681      long masterSystemTime = EnvironmentEdgeManager.currentTime() + 123456789;
682
683      // write the serverName columns
684      MetaTableAccessor.updateRegionLocation(connection, regionInfoA, sn, 1, serverNameTime);
685
686      // assert that we have the serverName column with expected ts
687      Get get = new Get(mergedRegionInfo.getRegionName());
688      Result result = meta.get(get);
689      Cell serverCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
690          MetaTableAccessor.getServerColumn(0));
691      assertNotNull(serverCell);
692      assertEquals(serverNameTime, serverCell.getTimestamp());
693
694      ManualEnvironmentEdge edge = new ManualEnvironmentEdge();
695      edge.setValue(masterSystemTime);
696      EnvironmentEdgeManager.injectEdge(edge);
697      try {
698        // now merge the regions, effectively deleting the rows for region a and b.
699        MetaTableAccessor.mergeRegions(connection, mergedRegionInfo, regionInfoA, regionInfoB, sn,
700          1);
701      } finally {
702        EnvironmentEdgeManager.reset();
703      }
704
705
706      result = meta.get(get);
707      serverCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
708          MetaTableAccessor.getServerColumn(0));
709      Cell startCodeCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
710        MetaTableAccessor.getStartCodeColumn(0));
711      Cell seqNumCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
712        MetaTableAccessor.getSeqNumColumn(0));
713      assertNull(serverCell);
714      assertNull(startCodeCell);
715      assertNull(seqNumCell);
716    }
717  }
718
719  public static class SpyingRpcSchedulerFactory extends SimpleRpcSchedulerFactory {
720    @Override
721    public RpcScheduler create(Configuration conf, PriorityFunction priority, Abortable server) {
722      final RpcScheduler delegate = super.create(conf, priority, server);
723      return new SpyingRpcScheduler(delegate);
724    }
725  }
726
727  public static class SpyingRpcScheduler extends DelegatingRpcScheduler {
728    long numPriorityCalls = 0;
729
730    public SpyingRpcScheduler(RpcScheduler delegate) {
731      super(delegate);
732    }
733
734    @Override
735    public boolean dispatch(CallRunner task) throws IOException, InterruptedException {
736      int priority = task.getRpcCall().getPriority();
737
738      if (priority > HConstants.QOS_THRESHOLD) {
739        numPriorityCalls++;
740      }
741      return super.dispatch(task);
742    }
743  }
744
745  @Test
746  public void testMetaUpdatesGoToPriorityQueue() throws Exception {
747    // This test has to be end-to-end, and do the verification from the server side
748    Configuration c = UTIL.getConfiguration();
749
750    c.set(RSRpcServices.REGION_SERVER_RPC_SCHEDULER_FACTORY_CLASS,
751      SpyingRpcSchedulerFactory.class.getName());
752
753    // restart so that new config takes place
754    afterClass();
755    beforeClass();
756
757    final TableName tableName = TableName.valueOf(name.getMethodName());
758    try (Admin admin = connection.getAdmin();
759        RegionLocator rl = connection.getRegionLocator(tableName)) {
760
761      // create a table and prepare for a manual split
762      UTIL.createTable(tableName, "cf1");
763
764      HRegionLocation loc = rl.getAllRegionLocations().get(0);
765      RegionInfo parent = loc.getRegionInfo();
766      long rid = 1000;
767      byte[] splitKey = Bytes.toBytes("a");
768      RegionInfo splitA = RegionInfoBuilder.newBuilder(parent.getTable())
769          .setStartKey(parent.getStartKey())
770          .setEndKey(splitKey)
771          .setSplit(false)
772          .setRegionId(rid)
773          .build();
774      RegionInfo splitB = RegionInfoBuilder.newBuilder(parent.getTable())
775          .setStartKey(splitKey)
776          .setEndKey(parent.getEndKey())
777          .setSplit(false)
778          .setRegionId(rid)
779          .build();
780
781      // find the meta server
782      MiniHBaseCluster cluster = UTIL.getMiniHBaseCluster();
783      int rsIndex = cluster.getServerWithMeta();
784      HRegionServer rs;
785      if (rsIndex >= 0) {
786        rs = cluster.getRegionServer(rsIndex);
787      } else {
788        // it is in master
789        rs = cluster.getMaster();
790      }
791      SpyingRpcScheduler scheduler = (SpyingRpcScheduler) rs.getRpcServer().getScheduler();
792      long prevCalls = scheduler.numPriorityCalls;
793      MetaTableAccessor.splitRegion(connection, parent, splitA, splitB, loc.getServerName(), 1);
794
795      assertTrue(prevCalls < scheduler.numPriorityCalls);
796    }
797  }
798
799  @Test
800  public void testEmptyMetaDaughterLocationDuringSplit() throws IOException {
801    long regionId = System.currentTimeMillis();
802    ServerName serverName0 = ServerName.valueOf("foo", 60010, random.nextLong());
803    RegionInfo parent = RegionInfoBuilder.newBuilder(TableName.valueOf("table_foo"))
804        .setStartKey(HConstants.EMPTY_START_ROW)
805        .setEndKey(HConstants.EMPTY_END_ROW)
806        .setSplit(false)
807        .setRegionId(regionId)
808        .setReplicaId(0)
809        .build();
810    RegionInfo splitA = RegionInfoBuilder.newBuilder(TableName.valueOf("table_foo"))
811        .setStartKey(HConstants.EMPTY_START_ROW)
812        .setEndKey(Bytes.toBytes("a"))
813        .setSplit(false)
814        .setRegionId(regionId + 1)
815        .setReplicaId(0)
816        .build();
817    RegionInfo splitB = RegionInfoBuilder.newBuilder(TableName.valueOf("table_foo"))
818        .setStartKey(Bytes.toBytes("a"))
819        .setEndKey(HConstants.EMPTY_END_ROW)
820        .setSplit(false)
821        .setRegionId(regionId + 1)
822        .setReplicaId(0)
823        .build();
824
825    Table meta = MetaTableAccessor.getMetaHTable(connection);
826    try {
827      List<RegionInfo> regionInfos = Lists.newArrayList(parent);
828      MetaTableAccessor.addRegionsToMeta(connection, regionInfos, 3);
829
830      MetaTableAccessor.splitRegion(connection, parent, splitA, splitB, serverName0, 3);
831      Get get1 = new Get(splitA.getRegionName());
832      Result resultA = meta.get(get1);
833      Cell serverCellA = resultA.getColumnLatestCell(HConstants.CATALOG_FAMILY,
834        MetaTableAccessor.getServerColumn(splitA.getReplicaId()));
835      Cell startCodeCellA = resultA.getColumnLatestCell(HConstants.CATALOG_FAMILY,
836        MetaTableAccessor.getStartCodeColumn(splitA.getReplicaId()));
837      assertNull(serverCellA);
838      assertNull(startCodeCellA);
839
840      Get get2 = new Get(splitA.getRegionName());
841      Result resultB = meta.get(get2);
842      Cell serverCellB = resultB.getColumnLatestCell(HConstants.CATALOG_FAMILY,
843        MetaTableAccessor.getServerColumn(splitB.getReplicaId()));
844      Cell startCodeCellB = resultB.getColumnLatestCell(HConstants.CATALOG_FAMILY,
845        MetaTableAccessor.getStartCodeColumn(splitB.getReplicaId()));
846      assertNull(serverCellB);
847      assertNull(startCodeCellB);
848    } finally {
849      if (meta != null) {
850        meta.close();
851      }
852    }
853  }
854}
855