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.replication.regionserver;
019
020import static org.apache.hadoop.hbase.client.RegionLocator.LOCATOR_META_REPLICAS_MODE;
021import static org.junit.Assert.assertArrayEquals;
022import static org.junit.Assert.assertEquals;
023import static org.junit.Assert.assertFalse;
024import static org.junit.Assert.assertNotNull;
025import static org.junit.Assert.assertTrue;
026
027import java.io.IOException;
028import java.util.ArrayList;
029import java.util.Arrays;
030import java.util.List;
031import java.util.Objects;
032import java.util.concurrent.TimeUnit;
033import org.apache.commons.lang3.mutable.MutableObject;
034import org.apache.hadoop.conf.Configuration;
035import org.apache.hadoop.hbase.Cell;
036import org.apache.hadoop.hbase.CellScanner;
037import org.apache.hadoop.hbase.CellUtil;
038import org.apache.hadoop.hbase.HBaseClassTestRule;
039import org.apache.hadoop.hbase.HBaseTestingUtility;
040import org.apache.hadoop.hbase.HConstants;
041import org.apache.hadoop.hbase.HRegionLocation;
042import org.apache.hadoop.hbase.MetaTableAccessor;
043import org.apache.hadoop.hbase.MiniHBaseCluster;
044import org.apache.hadoop.hbase.TableName;
045import org.apache.hadoop.hbase.Waiter;
046import org.apache.hadoop.hbase.client.Connection;
047import org.apache.hadoop.hbase.client.ConnectionFactory;
048import org.apache.hadoop.hbase.client.Get;
049import org.apache.hadoop.hbase.client.RegionInfo;
050import org.apache.hadoop.hbase.client.RegionLocator;
051import org.apache.hadoop.hbase.client.Result;
052import org.apache.hadoop.hbase.client.Scan;
053import org.apache.hadoop.hbase.client.Table;
054import org.apache.hadoop.hbase.regionserver.HRegion;
055import org.apache.hadoop.hbase.regionserver.HRegionServer;
056import org.apache.hadoop.hbase.regionserver.Region;
057import org.apache.hadoop.hbase.regionserver.RegionScanner;
058import org.apache.hadoop.hbase.replication.ReplicationPeerImpl;
059import org.apache.hadoop.hbase.testclassification.LargeTests;
060import org.apache.hadoop.hbase.util.Bytes;
061import org.apache.hadoop.hbase.util.ServerRegionReplicaUtil;
062import org.junit.After;
063import org.junit.Before;
064import org.junit.ClassRule;
065import org.junit.Rule;
066import org.junit.Test;
067import org.junit.experimental.categories.Category;
068import org.junit.rules.TestName;
069import org.slf4j.Logger;
070import org.slf4j.LoggerFactory;
071
072/**
073 * Tests RegionReplicaReplicationEndpoint class for hbase:meta by setting up region replicas and
074 * verifying async wal replication replays the edits to the secondary region in various scenarios.
075 * @see TestRegionReplicaReplicationEndpoint
076 */
077@Category({ LargeTests.class })
078public class TestMetaRegionReplicaReplicationEndpoint {
079  @ClassRule
080  public static final HBaseClassTestRule CLASS_RULE =
081    HBaseClassTestRule.forClass(TestMetaRegionReplicaReplicationEndpoint.class);
082  private static final Logger LOG =
083    LoggerFactory.getLogger(TestMetaRegionReplicaReplicationEndpoint.class);
084  private static final int NB_SERVERS = 4;
085  private final HBaseTestingUtility HTU = new HBaseTestingUtility();
086  private int numOfMetaReplica = NB_SERVERS - 1;
087  private static byte[] VALUE = Bytes.toBytes("value");
088
089  @Rule
090  public TestName name = new TestName();
091
092  @Before
093  public void before() throws Exception {
094    Configuration conf = HTU.getConfiguration();
095    conf.setFloat("hbase.regionserver.logroll.multiplier", 0.0003f);
096    conf.setInt("replication.source.size.capacity", 10240);
097    conf.setLong("replication.source.sleepforretries", 100);
098    conf.setInt("hbase.regionserver.maxlogs", 10);
099    conf.setLong("hbase.master.logcleaner.ttl", 10);
100    conf.setInt("zookeeper.recovery.retry", 1);
101    conf.setInt("zookeeper.recovery.retry.intervalmill", 10);
102    conf.setLong(HConstants.THREAD_WAKE_FREQUENCY, 100);
103    conf.setInt("replication.stats.thread.period.seconds", 5);
104    conf.setBoolean("hbase.tests.use.shortcircuit.reads", false);
105    conf.setInt(HConstants.HBASE_CLIENT_SERVERSIDE_RETRIES_MULTIPLIER, 1);
106    // Enable hbase:meta replication.
107    conf.setBoolean(ServerRegionReplicaUtil.REGION_REPLICA_REPLICATION_CATALOG_CONF_KEY, true);
108    // Set hbase:meta replicas to be 3.
109    // conf.setInt(HConstants.META_REPLICAS_NUM, numOfMetaReplica);
110    HTU.startMiniCluster(NB_SERVERS);
111    // Enable hbase:meta replication.
112    HBaseTestingUtility.setReplicas(HTU.getAdmin(), TableName.META_TABLE_NAME, numOfMetaReplica);
113
114    HTU.waitFor(30000, () -> HTU.getMiniHBaseCluster().getRegions(TableName.META_TABLE_NAME).size()
115        >= numOfMetaReplica);
116  }
117
118  @After
119  public void after() throws Exception {
120    HTU.shutdownMiniCluster();
121  }
122
123  /**
124   * Assert that the ReplicationSource for hbase:meta gets created when hbase:meta is opened.
125   */
126  @Test
127  public void testHBaseMetaReplicationSourceCreatedOnOpen() throws Exception {
128    MiniHBaseCluster cluster = HTU.getMiniHBaseCluster();
129    HRegionServer hrs = cluster.getRegionServer(cluster.getServerHoldingMeta());
130    // Replicate a row to prove all working.
131    testHBaseMetaReplicatesOneRow(0);
132    assertTrue(isMetaRegionReplicaReplicationSource(hrs));
133    // Now move the hbase:meta and make sure the ReplicationSource is in both places.
134    HRegionServer hrsOther = null;
135    for (int i = 0; i < cluster.getNumLiveRegionServers(); i++) {
136      hrsOther = cluster.getRegionServer(i);
137      if (hrsOther.getServerName().equals(hrs.getServerName())) {
138        hrsOther = null;
139        continue;
140      }
141      break;
142    }
143    assertNotNull(hrsOther);
144    assertFalse(isMetaRegionReplicaReplicationSource(hrsOther));
145    Region meta = null;
146    for (Region region : hrs.getOnlineRegionsLocalContext()) {
147      if (region.getRegionInfo().isMetaRegion()) {
148        meta = region;
149        break;
150      }
151    }
152    assertNotNull(meta);
153    HTU.moveRegionAndWait(meta.getRegionInfo(), hrsOther.getServerName());
154    // Assert that there is a ReplicationSource in both places now.
155    assertTrue(isMetaRegionReplicaReplicationSource(hrs));
156    assertTrue(isMetaRegionReplicaReplicationSource(hrsOther));
157    // Replicate to show stuff still works.
158    testHBaseMetaReplicatesOneRow(1);
159    // Now pretend a few hours have gone by... roll the meta WAL in original location... Move the
160    // meta back and retry replication. See if it works.
161    hrs.getWAL(meta.getRegionInfo()).rollWriter(true);
162    testHBaseMetaReplicatesOneRow(2);
163    hrs.getWAL(meta.getRegionInfo()).rollWriter(true);
164    testHBaseMetaReplicatesOneRow(3);
165  }
166
167  /**
168   * Test meta region replica replication. Create some tables and see if replicas pick up the
169   * additions.
170   */
171  private void testHBaseMetaReplicatesOneRow(int i) throws Exception {
172    waitForMetaReplicasToOnline();
173    try (Table table = HTU.createTable(TableName.valueOf(this.name.getMethodName() + "_" + i),
174      HConstants.CATALOG_FAMILY)) {
175      verifyReplication(TableName.META_TABLE_NAME, numOfMetaReplica, getMetaCells(table.getName()));
176    }
177  }
178
179  /** Returns Whether the special meta region replica peer is enabled on <code>hrs</code> */
180  private boolean isMetaRegionReplicaReplicationSource(HRegionServer hrs) {
181    return hrs.getReplicationSourceService().getReplicationManager().catalogReplicationSource.get()
182        != null;
183  }
184
185  /**
186   * Test meta region replica replication. Create some tables and see if replicas pick up the
187   * additions.
188   */
189  @Test
190  public void testHBaseMetaReplicates() throws Exception {
191    try (Table table = HTU.createTable(TableName.valueOf(this.name.getMethodName() + "_0"),
192      HConstants.CATALOG_FAMILY,
193      Arrays.copyOfRange(HBaseTestingUtility.KEYS, 1, HBaseTestingUtility.KEYS.length))) {
194      verifyReplication(TableName.META_TABLE_NAME, numOfMetaReplica, getMetaCells(table.getName()));
195    }
196    try (Table table = HTU.createTable(TableName.valueOf(this.name.getMethodName() + "_1"),
197      HConstants.CATALOG_FAMILY,
198      Arrays.copyOfRange(HBaseTestingUtility.KEYS, 1, HBaseTestingUtility.KEYS.length))) {
199      verifyReplication(TableName.META_TABLE_NAME, numOfMetaReplica, getMetaCells(table.getName()));
200      // Try delete.
201      HTU.deleteTableIfAny(table.getName());
202      verifyDeletedReplication(TableName.META_TABLE_NAME, numOfMetaReplica, table.getName());
203    }
204  }
205
206  @Test
207  public void testCatalogReplicaReplicationWithFlushAndCompaction() throws Exception {
208    Connection connection = ConnectionFactory.createConnection(HTU.getConfiguration());
209    TableName tableName = TableName.valueOf("hbase:meta");
210    Table table = connection.getTable(tableName);
211    try {
212      // load the data to the table
213      for (int i = 0; i < 5; i++) {
214        LOG.info("Writing data from " + i * 1000 + " to " + (i * 1000 + 1000));
215        HTU.loadNumericRows(table, HConstants.CATALOG_FAMILY, i * 1000, i * 1000 + 1000);
216        LOG.info("flushing table");
217        HTU.flush(tableName);
218        LOG.info("compacting table");
219        if (i < 4) {
220          HTU.compact(tableName, false);
221        }
222      }
223
224      verifyReplication(tableName, numOfMetaReplica, 0, 5000, HConstants.CATALOG_FAMILY);
225    } finally {
226      table.close();
227      connection.close();
228    }
229  }
230
231  @Test
232  public void testCatalogReplicaReplicationWALRolledAndDeleted() throws Exception {
233    TableName tableName = TableName.valueOf("hbase:meta");
234    try (Connection connection = ConnectionFactory.createConnection(HTU.getConfiguration());
235      Table table = connection.getTable(tableName)) {
236      MiniHBaseCluster cluster = HTU.getHBaseCluster();
237      cluster.getMaster().balanceSwitch(false);
238      HRegionServer hrs = cluster.getRegionServer(cluster.getServerHoldingMeta());
239      ReplicationSource source = (ReplicationSource) hrs.getReplicationSourceService()
240        .getReplicationManager().catalogReplicationSource.get();
241      ((ReplicationPeerImpl) source.replicationPeer).setPeerState(false);
242      // there's small chance source reader has passed the peer state check but not yet read the
243      // wal, which could allow it to read some added entries before the wal gets deleted,
244      // so we are making sure here we only proceed once the reader loop has managed to
245      // detect the peer is disabled.
246      HTU.waitFor(2000, 100, true, () -> {
247        MutableObject<Boolean> readerWaiting = new MutableObject<>(true);
248        source.logQueue.getQueues().keySet()
249          .forEach(w -> readerWaiting.setValue(readerWaiting.getValue()
250            && source.workerThreads.get(w).entryReader.waitingPeerEnabled.get()));
251        return readerWaiting.getValue();
252      });
253      // load the data to the table
254      for (int i = 0; i < 5; i++) {
255        LOG.info("Writing data from " + i * 1000 + " to " + (i * 1000 + 1000));
256        HTU.loadNumericRows(table, HConstants.CATALOG_FAMILY, i * 1000, i * 1000 + 1000);
257        LOG.info("flushing table");
258        HTU.flush(tableName);
259        LOG.info("compacting table");
260        if (i < 4) {
261          HTU.compact(tableName, false);
262        }
263      }
264      HTU.getHBaseCluster().getMaster().getLogCleaner().triggerCleanerNow().get(1,
265        TimeUnit.SECONDS);
266      ((ReplicationPeerImpl) source.replicationPeer).setPeerState(true);
267      // now loads more data without flushing nor compacting
268      for (int i = 5; i < 10; i++) {
269        LOG.info("Writing data from " + i * 1000 + " to " + (i * 1000 + 1000));
270        HTU.loadNumericRows(table, HConstants.CATALOG_FAMILY, i * 1000, i * 1000 + 1000);
271      }
272      verifyReplication(tableName, numOfMetaReplica, 0, 10000, HConstants.CATALOG_FAMILY);
273    }
274  }
275
276  @Test
277  public void testCatalogReplicaReplicationWithReplicaMoved() throws Exception {
278    MiniHBaseCluster cluster = HTU.getMiniHBaseCluster();
279    HRegionServer hrs = cluster.getRegionServer(cluster.getServerHoldingMeta());
280
281    HRegionServer hrsMetaReplica = null;
282    HRegionServer hrsNoMetaReplica = null;
283    HRegionServer server = null;
284    Region metaReplica = null;
285    boolean hostingMeta;
286
287    for (int i = 0; i < cluster.getNumLiveRegionServers(); i++) {
288      server = cluster.getRegionServer(i);
289      hostingMeta = false;
290      if (server == hrs) {
291        continue;
292      }
293      for (Region region : server.getOnlineRegionsLocalContext()) {
294        if (region.getRegionInfo().isMetaRegion()) {
295          if (metaReplica == null) {
296            metaReplica = region;
297          }
298          hostingMeta = true;
299          break;
300        }
301      }
302      if (!hostingMeta) {
303        hrsNoMetaReplica = server;
304      }
305    }
306
307    Connection connection = ConnectionFactory.createConnection(HTU.getConfiguration());
308    TableName tableName = TableName.valueOf("hbase:meta");
309    Table table = connection.getTable(tableName);
310    try {
311      // load the data to the table
312      for (int i = 0; i < 5; i++) {
313        LOG.info("Writing data from " + i * 1000 + " to " + (i * 1000 + 1000));
314        HTU.loadNumericRows(table, HConstants.CATALOG_FAMILY, i * 1000, i * 1000 + 1000);
315        if (i == 0) {
316          HTU.moveRegionAndWait(metaReplica.getRegionInfo(), hrsNoMetaReplica.getServerName());
317        }
318      }
319
320      verifyReplication(tableName, numOfMetaReplica, 0, 5000, HConstants.CATALOG_FAMILY);
321    } finally {
322      table.close();
323      connection.close();
324    }
325  }
326
327  protected void verifyReplication(TableName tableName, int regionReplication, final int startRow,
328    final int endRow, final byte[] family) throws Exception {
329    verifyReplication(tableName, regionReplication, startRow, endRow, family, true);
330  }
331
332  private void verifyReplication(TableName tableName, int regionReplication, final int startRow,
333    final int endRow, final byte[] family, final boolean present) throws Exception {
334    // find the regions
335    final Region[] regions = new Region[regionReplication];
336
337    for (int i = 0; i < NB_SERVERS; i++) {
338      HRegionServer rs = HTU.getMiniHBaseCluster().getRegionServer(i);
339      List<HRegion> onlineRegions = rs.getRegions(tableName);
340      for (HRegion region : onlineRegions) {
341        regions[region.getRegionInfo().getReplicaId()] = region;
342      }
343    }
344
345    for (Region region : regions) {
346      assertNotNull(region);
347    }
348
349    for (int i = 1; i < regionReplication; i++) {
350      final Region region = regions[i];
351      // wait until all the data is replicated to all secondary regions
352      Waiter.waitFor(HTU.getConfiguration(), 90000, 1000, new Waiter.Predicate<Exception>() {
353        @Override
354        public boolean evaluate() throws Exception {
355          LOG.info("verifying replication for region replica:" + region.getRegionInfo());
356          try {
357            HTU.verifyNumericRows(region, family, startRow, endRow, present);
358          } catch (Throwable ex) {
359            LOG.warn("Verification from secondary region is not complete yet", ex);
360            // still wait
361            return false;
362          }
363          return true;
364        }
365      });
366    }
367  }
368
369  /**
370   * Replicas come online after primary.
371   */
372  private void waitForMetaReplicasToOnline() throws IOException {
373    final RegionLocator regionLocator =
374      HTU.getConnection().getRegionLocator(TableName.META_TABLE_NAME);
375    HTU.waitFor(10000,
376      // getRegionLocations returns an entry for each replica but if unassigned, entry is null.
377      // Pass reload to force us to skip cache else it just keeps returning default.
378      () -> regionLocator.getRegionLocations(HConstants.EMPTY_START_ROW, true).stream()
379        .filter(Objects::nonNull).count() >= numOfMetaReplica);
380    List<HRegionLocation> locations = regionLocator.getRegionLocations(HConstants.EMPTY_START_ROW);
381    LOG.info("Found locations {}", locations);
382    assertEquals(numOfMetaReplica, locations.size());
383  }
384
385  /**
386   * Scan hbase:meta for <code>tableName</code> content.
387   */
388  private List<Result> getMetaCells(TableName tableName) throws IOException {
389    final List<Result> results = new ArrayList<>();
390    MetaTableAccessor.Visitor visitor = new MetaTableAccessor.Visitor() {
391      @Override
392      public boolean visit(Result r) throws IOException {
393        results.add(r);
394        return true;
395      }
396    };
397    MetaTableAccessor.scanMetaForTableRegions(HTU.getConnection(), visitor, tableName);
398    return results;
399  }
400
401  /** Returns All Regions for tableName including Replicas. */
402  private Region[] getAllRegions(TableName tableName, int replication) {
403    final Region[] regions = new Region[replication];
404    for (int i = 0; i < NB_SERVERS; i++) {
405      HRegionServer rs = HTU.getMiniHBaseCluster().getRegionServer(i);
406      List<HRegion> onlineRegions = rs.getRegions(tableName);
407      for (HRegion region : onlineRegions) {
408        regions[region.getRegionInfo().getReplicaId()] = region;
409      }
410    }
411    for (Region region : regions) {
412      assertNotNull(region);
413    }
414    return regions;
415  }
416
417  private Region getOneRegion(TableName tableName) {
418    for (int i = 0; i < NB_SERVERS; i++) {
419      HRegionServer rs = HTU.getMiniHBaseCluster().getRegionServer(i);
420      List<HRegion> onlineRegions = rs.getRegions(tableName);
421      if (onlineRegions.size() > 1) {
422        return onlineRegions.get(0);
423      }
424    }
425    return null;
426  }
427
428  /**
429   * Verify when a Table is deleted from primary, then there are no references in replicas (because
430   * they get the delete of the table rows too).
431   */
432  private void verifyDeletedReplication(TableName tableName, int regionReplication,
433    final TableName deletedTableName) {
434    final Region[] regions = getAllRegions(tableName, regionReplication);
435
436    // Start count at '1' so we skip default, primary replica and only look at secondaries.
437    for (int i = 1; i < regionReplication; i++) {
438      final Region region = regions[i];
439      // wait until all the data is replicated to all secondary regions
440      Waiter.waitFor(HTU.getConfiguration(), 30000, 1000, new Waiter.Predicate<Exception>() {
441        @Override
442        public boolean evaluate() throws Exception {
443          LOG.info("Verifying replication for region replica {}", region.getRegionInfo());
444          try (RegionScanner rs = region.getScanner(new Scan())) {
445            List<Cell> cells = new ArrayList<>();
446            while (rs.next(cells)) {
447              continue;
448            }
449            return doesNotContain(cells, deletedTableName);
450          } catch (Throwable ex) {
451            LOG.warn("Verification from secondary region is not complete yet", ex);
452            // still wait
453            return false;
454          }
455        }
456      });
457    }
458  }
459
460  /**
461   * Cells are from hbase:meta replica so will start w/ 'tableName,'; i.e. the tablename followed by
462   * HConstants.DELIMITER. Make sure the deleted table is no longer present in passed
463   * <code>cells</code>.
464   */
465  private boolean doesNotContain(List<Cell> cells, TableName tableName) {
466    for (Cell cell : cells) {
467      String row = Bytes.toString(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
468      if (row.startsWith(tableName.toString() + HConstants.DELIMITER)) {
469        return false;
470      }
471    }
472    return true;
473  }
474
475  /**
476   * Verify Replicas have results (exactly).
477   */
478  private void verifyReplication(TableName tableName, int regionReplication,
479    List<Result> contains) {
480    final Region[] regions = getAllRegions(tableName, regionReplication);
481
482    // Start count at '1' so we skip default, primary replica and only look at secondaries.
483    for (int i = 1; i < regionReplication; i++) {
484      final Region region = regions[i];
485      // wait until all the data is replicated to all secondary regions
486      Waiter.waitFor(HTU.getConfiguration(), 30000, 1000, new Waiter.Predicate<Exception>() {
487        @Override
488        public boolean evaluate() throws Exception {
489          LOG.info("Verifying replication for region replica {}", region.getRegionInfo());
490          try (RegionScanner rs = region.getScanner(new Scan())) {
491            List<Cell> cells = new ArrayList<>();
492            while (rs.next(cells)) {
493              continue;
494            }
495            return contains(contains, cells);
496          } catch (Throwable ex) {
497            LOG.warn("Verification from secondary region is not complete yet", ex);
498            // still wait
499            return false;
500          }
501        }
502      });
503    }
504  }
505
506  /**
507   * Presumes sorted Cells. Verify that <code>cells</code> has <code>contains</code> at least.
508   */
509  static boolean contains(List<Result> contains, List<Cell> cells) throws IOException {
510    CellScanner containsScanner = CellUtil.createCellScanner(contains);
511    CellScanner cellsScanner = CellUtil.createCellScanner(cells);
512    int matches = 0;
513    int count = 0;
514    while (containsScanner.advance()) {
515      while (cellsScanner.advance()) {
516        count++;
517        LOG.info("{} {}", containsScanner.current(), cellsScanner.current());
518        if (containsScanner.current().equals(cellsScanner.current())) {
519          matches++;
520          break;
521        }
522      }
523    }
524    return !containsScanner.advance() && matches >= 1 && count >= matches && count == cells.size();
525  }
526
527  private void doNGets(final Table table, final byte[][] keys) throws Exception {
528    for (byte[] key : keys) {
529      Result r = table.get(new Get(key));
530      assertArrayEquals(VALUE, r.getValue(HConstants.CATALOG_FAMILY, HConstants.CATALOG_FAMILY));
531    }
532  }
533
534  private void primaryNoChangeReplicaIncrease(final long[] before, final long[] after) {
535    assertEquals(before[RegionInfo.DEFAULT_REPLICA_ID], after[RegionInfo.DEFAULT_REPLICA_ID]);
536
537    for (int i = 1; i < after.length; i++) {
538      assertTrue(after[i] > before[i]);
539    }
540  }
541
542  private void primaryIncreaseReplicaNoChange(final long[] before, final long[] after) {
543    // There are read requests increase for primary meta replica.
544    assertTrue(after[RegionInfo.DEFAULT_REPLICA_ID] > before[RegionInfo.DEFAULT_REPLICA_ID]);
545
546    // No change for replica regions
547    for (int i = 1; i < after.length; i++) {
548      assertEquals(before[i], after[i]);
549    }
550  }
551
552  private void primaryMayIncreaseReplicaNoChange(final long[] before, final long[] after) {
553    // For primary meta replica, scan request may increase. No change for replica meta regions.
554    assertTrue(after[RegionInfo.DEFAULT_REPLICA_ID] >= before[RegionInfo.DEFAULT_REPLICA_ID]);
555
556    // No change for replica regions
557    for (int i = 1; i < after.length; i++) {
558      assertEquals(before[i], after[i]);
559    }
560  }
561
562  private void primaryIncreaseReplicaIncrease(final long[] before, final long[] after) {
563    // There are read requests increase for all meta replica regions,
564    for (int i = 0; i < after.length; i++) {
565      assertTrue(after[i] > before[i]);
566    }
567  }
568
569  private void getMetaReplicaReadRequests(final Region[] metaRegions, final long[] counters) {
570    int i = 0;
571    for (Region r : metaRegions) {
572      LOG.info("read request for region {} is {}", r, r.getReadRequestsCount());
573      counters[i] = r.getReadRequestsCount();
574      i++;
575    }
576  }
577
578  @Test
579  public void testHBaseMetaReplicaGets() throws Exception {
580
581    TableName tn = TableName.valueOf(this.name.getMethodName());
582    final Region[] metaRegions = getAllRegions(TableName.META_TABLE_NAME, numOfMetaReplica);
583    long[] readReqsForMetaReplicas = new long[numOfMetaReplica];
584    long[] readReqsForMetaReplicasAfterGet = new long[numOfMetaReplica];
585    long[] readReqsForMetaReplicasAfterGetAllLocations = new long[numOfMetaReplica];
586    long[] readReqsForMetaReplicasAfterMove = new long[numOfMetaReplica];
587    long[] readReqsForMetaReplicasAfterSecondMove = new long[numOfMetaReplica];
588    long[] readReqsForMetaReplicasAfterThirdGet = new long[numOfMetaReplica];
589    Region userRegion = null;
590    HRegionServer srcRs = null;
591    HRegionServer destRs = null;
592
593    try (Table table = HTU.createTable(tn, HConstants.CATALOG_FAMILY,
594      Arrays.copyOfRange(HBaseTestingUtility.KEYS, 1, HBaseTestingUtility.KEYS.length))) {
595      verifyReplication(TableName.META_TABLE_NAME, numOfMetaReplica, getMetaCells(table.getName()));
596      // load different values
597      HTU.loadTable(table, new byte[][] { HConstants.CATALOG_FAMILY }, VALUE);
598      for (int i = 0; i < NB_SERVERS; i++) {
599        HRegionServer rs = HTU.getMiniHBaseCluster().getRegionServer(i);
600        List<HRegion> onlineRegions = rs.getRegions(tn);
601        if (onlineRegions.size() > 0) {
602          userRegion = onlineRegions.get(0);
603          srcRs = rs;
604          if (i > 0) {
605            destRs = HTU.getMiniHBaseCluster().getRegionServer(0);
606          } else {
607            destRs = HTU.getMiniHBaseCluster().getRegionServer(1);
608          }
609        }
610      }
611
612      getMetaReplicaReadRequests(metaRegions, readReqsForMetaReplicas);
613
614      Configuration c = new Configuration(HTU.getConfiguration());
615      c.set(LOCATOR_META_REPLICAS_MODE, "LoadBalance");
616      Connection connection = ConnectionFactory.createConnection(c);
617      Table tableForGet = connection.getTable(tn);
618      byte[][] getRows = new byte[HBaseTestingUtility.KEYS.length][];
619
620      int i = 0;
621      for (byte[] key : HBaseTestingUtility.KEYS) {
622        getRows[i] = key;
623        i++;
624      }
625      getRows[0] = Bytes.toBytes("aaa");
626      doNGets(tableForGet, getRows);
627
628      getMetaReplicaReadRequests(metaRegions, readReqsForMetaReplicasAfterGet);
629
630      // There are more reads against all meta replica regions, including the primary region.
631      primaryIncreaseReplicaIncrease(readReqsForMetaReplicas, readReqsForMetaReplicasAfterGet);
632
633      RegionLocator locator = tableForGet.getRegionLocator();
634
635      for (int j = 0; j < numOfMetaReplica * 3; j++) {
636        locator.getAllRegionLocations();
637      }
638
639      getMetaReplicaReadRequests(metaRegions, readReqsForMetaReplicasAfterGetAllLocations);
640      primaryIncreaseReplicaIncrease(readReqsForMetaReplicasAfterGet,
641        readReqsForMetaReplicasAfterGetAllLocations);
642
643      // move one of regions so it meta cache may be invalid.
644      HTU.moveRegionAndWait(userRegion.getRegionInfo(), destRs.getServerName());
645
646      doNGets(tableForGet, getRows);
647
648      getMetaReplicaReadRequests(metaRegions, readReqsForMetaReplicasAfterMove);
649
650      // There are read requests increase for primary meta replica.
651      // For rest of meta replicas, there is no change as regionMove will tell the new location
652      primaryIncreaseReplicaNoChange(readReqsForMetaReplicasAfterGetAllLocations,
653        readReqsForMetaReplicasAfterMove);
654      // Move region again.
655      HTU.moveRegionAndWait(userRegion.getRegionInfo(), srcRs.getServerName());
656
657      // Wait until moveRegion cache timeout.
658      while (destRs.getMovedRegion(userRegion.getRegionInfo().getEncodedName()) != null) {
659        Thread.sleep(1000);
660      }
661
662      getMetaReplicaReadRequests(metaRegions, readReqsForMetaReplicasAfterSecondMove);
663
664      // There may be read requests increase for primary meta replica.
665      // For rest of meta replicas, there is no change.
666      primaryMayIncreaseReplicaNoChange(readReqsForMetaReplicasAfterMove,
667        readReqsForMetaReplicasAfterSecondMove);
668
669      doNGets(tableForGet, getRows);
670
671      getMetaReplicaReadRequests(metaRegions, readReqsForMetaReplicasAfterThirdGet);
672
673      // Since it gets RegionNotServedException, it will go to primary for the next lookup.
674      // There are read requests increase for primary meta replica.
675      // For rest of meta replicas, there is no change.
676      primaryIncreaseReplicaNoChange(readReqsForMetaReplicasAfterSecondMove,
677        readReqsForMetaReplicasAfterThirdGet);
678    }
679  }
680}