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