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