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