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.hamcrest.MatcherAssert.assertThat; 022import static org.hamcrest.Matchers.greaterThan; 023import static org.junit.jupiter.api.Assertions.assertArrayEquals; 024import static org.junit.jupiter.api.Assertions.assertEquals; 025import static org.junit.jupiter.api.Assertions.assertNotNull; 026 027import java.io.IOException; 028import java.util.ArrayList; 029import java.util.Arrays; 030import java.util.List; 031import java.util.concurrent.Callable; 032import org.apache.hadoop.conf.Configuration; 033import org.apache.hadoop.hbase.Cell; 034import org.apache.hadoop.hbase.CellScanner; 035import org.apache.hadoop.hbase.CellUtil; 036import org.apache.hadoop.hbase.ClientMetaTableAccessor; 037import org.apache.hadoop.hbase.HBaseTestingUtil; 038import org.apache.hadoop.hbase.HConstants; 039import org.apache.hadoop.hbase.MetaTableAccessor; 040import org.apache.hadoop.hbase.SingleProcessHBaseCluster; 041import org.apache.hadoop.hbase.TableName; 042import org.apache.hadoop.hbase.Waiter; 043import org.apache.hadoop.hbase.client.Connection; 044import org.apache.hadoop.hbase.client.ConnectionFactory; 045import org.apache.hadoop.hbase.client.Get; 046import org.apache.hadoop.hbase.client.RegionInfo; 047import org.apache.hadoop.hbase.client.RegionLocator; 048import org.apache.hadoop.hbase.client.Result; 049import org.apache.hadoop.hbase.client.Scan; 050import org.apache.hadoop.hbase.client.Table; 051import org.apache.hadoop.hbase.regionserver.HRegion; 052import org.apache.hadoop.hbase.regionserver.HRegionServer; 053import org.apache.hadoop.hbase.regionserver.Region; 054import org.apache.hadoop.hbase.regionserver.RegionScanner; 055import org.apache.hadoop.hbase.testclassification.LargeTests; 056import org.apache.hadoop.hbase.testclassification.ReplicationTests; 057import org.apache.hadoop.hbase.util.Bytes; 058import org.apache.hadoop.hbase.util.ServerRegionReplicaUtil; 059import org.junit.jupiter.api.AfterEach; 060import org.junit.jupiter.api.BeforeEach; 061import org.junit.jupiter.api.Tag; 062import org.junit.jupiter.api.Test; 063import org.junit.jupiter.api.TestInfo; 064import org.slf4j.Logger; 065import org.slf4j.LoggerFactory; 066 067/** 068 * Tests region replication for hbase:meta by setting up region replicas and verifying async wal 069 * replication replays the edits to the secondary region in various scenarios. 070 * @see TestRegionReplicaReplication 071 */ 072@Tag(ReplicationTests.TAG) 073@Tag(LargeTests.TAG) 074public class TestMetaRegionReplicaReplication { 075 076 private static final Logger LOG = LoggerFactory.getLogger(TestMetaRegionReplicaReplication.class); 077 private static final int NB_SERVERS = 4; 078 private final HBaseTestingUtil HTU = new HBaseTestingUtil(); 079 private int numOfMetaReplica = NB_SERVERS - 1; 080 private static byte[] VALUE = Bytes.toBytes("value"); 081 082 private String testName; 083 084 @BeforeEach 085 public void before(TestInfo testInfo) throws Exception { 086 testName = testInfo.getTestMethod().get().getName(); 087 Configuration conf = HTU.getConfiguration(); 088 conf.setInt("zookeeper.recovery.retry", 1); 089 conf.setInt("zookeeper.recovery.retry.intervalmill", 10); 090 conf.setLong(HConstants.THREAD_WAKE_FREQUENCY, 100); 091 conf.setBoolean("hbase.tests.use.shortcircuit.reads", false); 092 conf.setInt(HConstants.HBASE_CLIENT_SERVERSIDE_RETRIES_MULTIPLIER, 1); 093 // Enable hbase:meta replication. 094 conf.setBoolean(ServerRegionReplicaUtil.REGION_REPLICA_REPLICATION_CATALOG_CONF_KEY, true); 095 // Set hbase:meta replicas to be 3. 096 // conf.setInt(HConstants.META_REPLICAS_NUM, numOfMetaReplica); 097 HTU.startMiniCluster(NB_SERVERS); 098 // Enable hbase:meta replication. 099 HBaseTestingUtil.setReplicas(HTU.getAdmin(), TableName.META_TABLE_NAME, numOfMetaReplica); 100 101 HTU.waitFor(30000, () -> HTU.getMiniHBaseCluster().getRegions(TableName.META_TABLE_NAME).size() 102 >= numOfMetaReplica); 103 } 104 105 @AfterEach 106 public void after() throws Exception { 107 HTU.shutdownMiniCluster(); 108 } 109 110 /** 111 * Test meta region replica replication. Create some tables and see if replicas pick up the 112 * additions. 113 */ 114 @Test 115 public void testHBaseMetaReplicates() throws Exception { 116 try ( 117 Table table = HTU.createTable(TableName.valueOf(testName + "_0"), HConstants.CATALOG_FAMILY, 118 Arrays.copyOfRange(HBaseTestingUtil.KEYS, 1, HBaseTestingUtil.KEYS.length))) { 119 verifyReplication(TableName.META_TABLE_NAME, numOfMetaReplica, getMetaCells(table.getName())); 120 } 121 try ( 122 Table table = HTU.createTable(TableName.valueOf(testName + "_1"), HConstants.CATALOG_FAMILY, 123 Arrays.copyOfRange(HBaseTestingUtil.KEYS, 1, HBaseTestingUtil.KEYS.length))) { 124 verifyReplication(TableName.META_TABLE_NAME, numOfMetaReplica, getMetaCells(table.getName())); 125 // Try delete. 126 HTU.deleteTableIfAny(table.getName()); 127 verifyDeletedReplication(TableName.META_TABLE_NAME, numOfMetaReplica, table.getName()); 128 } 129 } 130 131 @Test 132 public void testCatalogReplicaReplicationWithFlushAndCompaction() throws Exception { 133 try (Connection connection = ConnectionFactory.createConnection(HTU.getConfiguration()); 134 Table table = connection.getTable(TableName.META_TABLE_NAME)) { 135 // load the data to the table 136 for (int i = 0; i < 5; i++) { 137 LOG.info("Writing data from " + i * 1000 + " to " + (i * 1000 + 1000)); 138 HTU.loadNumericRows(table, HConstants.CATALOG_FAMILY, i * 1000, i * 1000 + 1000); 139 LOG.info("flushing table"); 140 HTU.flush(TableName.META_TABLE_NAME); 141 LOG.info("compacting table"); 142 if (i < 4) { 143 HTU.compact(TableName.META_TABLE_NAME, false); 144 } 145 } 146 147 verifyReplication(TableName.META_TABLE_NAME, numOfMetaReplica, 0, 5000, 148 HConstants.CATALOG_FAMILY); 149 } 150 } 151 152 @Test 153 public void testCatalogReplicaReplicationWithReplicaMoved() throws Exception { 154 SingleProcessHBaseCluster cluster = HTU.getMiniHBaseCluster(); 155 HRegionServer hrs = cluster.getRegionServer(cluster.getServerHoldingMeta()); 156 157 HRegionServer hrsNoMetaReplica = null; 158 HRegionServer server = null; 159 Region metaReplica = null; 160 boolean hostingMeta; 161 162 for (int i = 0; i < cluster.getNumLiveRegionServers(); i++) { 163 server = cluster.getRegionServer(i); 164 hostingMeta = false; 165 if (server == hrs) { 166 continue; 167 } 168 for (Region region : server.getOnlineRegionsLocalContext()) { 169 if (region.getRegionInfo().isMetaRegion()) { 170 if (metaReplica == null) { 171 metaReplica = region; 172 } 173 hostingMeta = true; 174 break; 175 } 176 } 177 if (!hostingMeta) { 178 hrsNoMetaReplica = server; 179 } 180 } 181 try (Connection connection = ConnectionFactory.createConnection(HTU.getConfiguration()); 182 Table table = connection.getTable(TableName.META_TABLE_NAME)) { 183 // load the data to the table 184 for (int i = 0; i < 5; i++) { 185 LOG.info("Writing data from " + i * 1000 + " to " + (i * 1000 + 1000)); 186 HTU.loadNumericRows(table, HConstants.CATALOG_FAMILY, i * 1000, i * 1000 + 1000); 187 if (i == 0) { 188 HTU.moveRegionAndWait(metaReplica.getRegionInfo(), hrsNoMetaReplica.getServerName()); 189 } 190 } 191 192 verifyReplication(TableName.META_TABLE_NAME, numOfMetaReplica, 0, 5000, 193 HConstants.CATALOG_FAMILY); 194 } 195 } 196 197 protected void verifyReplication(TableName tableName, int regionReplication, final int startRow, 198 final int endRow, final byte[] family) throws Exception { 199 verifyReplication(tableName, regionReplication, startRow, endRow, family, true); 200 } 201 202 private void verifyReplication(TableName tableName, int regionReplication, final int startRow, 203 final int endRow, final byte[] family, final boolean present) throws Exception { 204 // find the regions 205 final Region[] regions = new Region[regionReplication]; 206 207 for (int i = 0; i < NB_SERVERS; i++) { 208 HRegionServer rs = HTU.getMiniHBaseCluster().getRegionServer(i); 209 List<HRegion> onlineRegions = rs.getRegions(tableName); 210 for (HRegion region : onlineRegions) { 211 regions[region.getRegionInfo().getReplicaId()] = region; 212 } 213 } 214 215 for (Region region : regions) { 216 assertNotNull(region); 217 } 218 219 for (int i = 1; i < regionReplication; i++) { 220 final Region region = regions[i]; 221 // wait until all the data is replicated to all secondary regions 222 Waiter.waitFor(HTU.getConfiguration(), 90000, 1000, new Waiter.Predicate<Exception>() { 223 @Override 224 public boolean evaluate() throws Exception { 225 LOG.info("verifying replication for region replica:" + region.getRegionInfo()); 226 try { 227 HTU.verifyNumericRows(region, family, startRow, endRow, present); 228 } catch (Throwable ex) { 229 LOG.warn("Verification from secondary region is not complete yet", ex); 230 // still wait 231 return false; 232 } 233 return true; 234 } 235 }); 236 } 237 } 238 239 /** 240 * Scan hbase:meta for <code>tableName</code> content. 241 */ 242 private List<Result> getMetaCells(TableName tableName) throws IOException { 243 final List<Result> results = new ArrayList<>(); 244 ClientMetaTableAccessor.Visitor visitor = new ClientMetaTableAccessor.Visitor() { 245 @Override 246 public boolean visit(Result r) throws IOException { 247 results.add(r); 248 return true; 249 } 250 }; 251 MetaTableAccessor.scanMetaForTableRegions(HTU.getConnection(), visitor, tableName); 252 return results; 253 } 254 255 /** Returns All Regions for tableName including Replicas. */ 256 private Region[] getAllRegions(TableName tableName, int replication) { 257 final Region[] regions = new Region[replication]; 258 for (int i = 0; i < NB_SERVERS; i++) { 259 HRegionServer rs = HTU.getMiniHBaseCluster().getRegionServer(i); 260 List<HRegion> onlineRegions = rs.getRegions(tableName); 261 for (HRegion region : onlineRegions) { 262 regions[region.getRegionInfo().getReplicaId()] = region; 263 } 264 } 265 for (Region region : regions) { 266 assertNotNull(region); 267 } 268 return regions; 269 } 270 271 /** 272 * Verify when a Table is deleted from primary, then there are no references in replicas (because 273 * they get the delete of the table rows too). 274 */ 275 private void verifyDeletedReplication(TableName tableName, int regionReplication, 276 final TableName deletedTableName) { 277 final Region[] regions = getAllRegions(tableName, regionReplication); 278 279 // Start count at '1' so we skip default, primary replica and only look at secondaries. 280 for (int i = 1; i < regionReplication; i++) { 281 final Region region = regions[i]; 282 // wait until all the data is replicated to all secondary regions 283 Waiter.waitFor(HTU.getConfiguration(), 30000, 1000, new Waiter.Predicate<Exception>() { 284 @Override 285 public boolean evaluate() throws Exception { 286 LOG.info("Verifying replication for region replica {}", region.getRegionInfo()); 287 try (RegionScanner rs = region.getScanner(new Scan())) { 288 List<Cell> cells = new ArrayList<>(); 289 while (rs.next(cells)) { 290 continue; 291 } 292 return doesNotContain(cells, deletedTableName); 293 } catch (Throwable ex) { 294 LOG.warn("Verification from secondary region is not complete yet", ex); 295 // still wait 296 return false; 297 } 298 } 299 }); 300 } 301 } 302 303 /** 304 * Cells are from hbase:meta replica so will start w/ 'tableName,'; i.e. the tablename followed by 305 * HConstants.DELIMITER. Make sure the deleted table is no longer present in passed 306 * <code>cells</code>. 307 */ 308 private boolean doesNotContain(List<Cell> cells, TableName tableName) { 309 for (Cell cell : cells) { 310 String row = Bytes.toString(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength()); 311 if (row.startsWith(tableName.toString() + HConstants.DELIMITER)) { 312 return false; 313 } 314 } 315 return true; 316 } 317 318 /** 319 * Verify Replicas have results (exactly). 320 */ 321 private void verifyReplication(TableName tableName, int regionReplication, 322 List<Result> contains) { 323 final Region[] regions = getAllRegions(tableName, regionReplication); 324 325 // Start count at '1' so we skip default, primary replica and only look at secondaries. 326 for (int i = 1; i < regionReplication; i++) { 327 final Region region = regions[i]; 328 // wait until all the data is replicated to all secondary regions 329 Waiter.waitFor(HTU.getConfiguration(), 30000, 1000, new Waiter.Predicate<Exception>() { 330 @Override 331 public boolean evaluate() throws Exception { 332 LOG.info("Verifying replication for region replica {}", region.getRegionInfo()); 333 try (RegionScanner rs = region.getScanner(new Scan())) { 334 List<Cell> cells = new ArrayList<>(); 335 while (rs.next(cells)) { 336 continue; 337 } 338 return contains(contains, cells); 339 } catch (Throwable ex) { 340 LOG.warn("Verification from secondary region is not complete yet", ex); 341 // still wait 342 return false; 343 } 344 } 345 }); 346 } 347 } 348 349 /** 350 * Presumes sorted Cells. Verify that <code>cells</code> has <code>contains</code> at least. 351 */ 352 static boolean contains(List<Result> contains, List<Cell> cells) throws IOException { 353 CellScanner containsScanner = CellUtil.createCellScanner(contains); 354 CellScanner cellsScanner = CellUtil.createCellScanner(cells); 355 int matches = 0; 356 int count = 0; 357 while (containsScanner.advance()) { 358 while (cellsScanner.advance()) { 359 count++; 360 LOG.info("{} {}", containsScanner.current(), cellsScanner.current()); 361 if (containsScanner.current().equals(cellsScanner.current())) { 362 matches++; 363 break; 364 } 365 } 366 } 367 return !containsScanner.advance() && matches >= 1 && count >= matches && count == cells.size(); 368 } 369 370 private void doNGets(final Table table, final byte[][] keys) throws Exception { 371 for (byte[] key : keys) { 372 Result r = table.get(new Get(key)); 373 assertArrayEquals(VALUE, r.getValue(HConstants.CATALOG_FAMILY, HConstants.CATALOG_FAMILY)); 374 } 375 } 376 377 private void primaryIncreaseReplicaNoChange(final long[] before, final long[] after) { 378 // There are read requests increase for primary meta replica. 379 assertThat(after[RegionInfo.DEFAULT_REPLICA_ID], 380 greaterThan(before[RegionInfo.DEFAULT_REPLICA_ID])); 381 382 // No change for replica regions 383 for (int i = 1; i < after.length; i++) { 384 assertEquals(before[i], after[i]); 385 } 386 } 387 388 private void primaryIncreaseReplicaIncrease(final long[] before, final long[] after) { 389 // There are read requests increase for all meta replica regions, 390 for (int i = 0; i < after.length; i++) { 391 assertThat(after[i], greaterThan(before[i])); 392 } 393 } 394 395 private void getMetaReplicaReadRequests(final Region[] metaRegions, final long[] counters) { 396 int i = 0; 397 for (Region r : metaRegions) { 398 LOG.info("read request for region {} is {}", r, r.getReadRequestsCount()); 399 counters[i] = r.getReadRequestsCount(); 400 i++; 401 } 402 } 403 404 private void runAtMostNTimes(int times, Callable<?> action, Runnable assertion) throws Exception { 405 for (int i = 0; i < times - 1; i++) { 406 action.call(); 407 try { 408 assertion.run(); 409 // return if the assertion passes, otherwise try again 410 return; 411 } catch (AssertionError e) { 412 LOG.warn("Assertion failed, times = {}, try again", i, e); 413 } 414 } 415 // try last time 416 action.call(); 417 assertion.run(); 418 } 419 420 @Test 421 public void testHBaseMetaReplicaGets() throws Exception { 422 TableName tn = TableName.valueOf(testName); 423 final Region[] metaRegions = getAllRegions(TableName.META_TABLE_NAME, numOfMetaReplica); 424 long[] readReqsForMetaReplicas = new long[numOfMetaReplica]; 425 long[] readReqsForMetaReplicasAfterGet = new long[numOfMetaReplica]; 426 long[] readReqsForMetaReplicasAfterGetAllLocations = new long[numOfMetaReplica]; 427 long[] readReqsForMetaReplicasAfterMove = new long[numOfMetaReplica]; 428 long[] readReqsForMetaReplicasAfterSecondMove = new long[numOfMetaReplica]; 429 long[] readReqsForMetaReplicasAfterThirdGet = new long[numOfMetaReplica]; 430 Region userRegion = null; 431 HRegionServer srcRs = null; 432 HRegionServer destRs = null; 433 434 try (Table table = HTU.createTable(tn, HConstants.CATALOG_FAMILY, 435 Arrays.copyOfRange(HBaseTestingUtil.KEYS, 1, HBaseTestingUtil.KEYS.length))) { 436 verifyReplication(TableName.META_TABLE_NAME, numOfMetaReplica, getMetaCells(table.getName())); 437 // load different values 438 HTU.loadTable(table, new byte[][] { HConstants.CATALOG_FAMILY }, VALUE); 439 for (int i = 0; i < NB_SERVERS; i++) { 440 HRegionServer rs = HTU.getMiniHBaseCluster().getRegionServer(i); 441 List<HRegion> onlineRegions = rs.getRegions(tn); 442 if (onlineRegions.size() > 0) { 443 userRegion = onlineRegions.get(0); 444 srcRs = rs; 445 if (i > 0) { 446 destRs = HTU.getMiniHBaseCluster().getRegionServer(0); 447 } else { 448 destRs = HTU.getMiniHBaseCluster().getRegionServer(1); 449 } 450 } 451 } 452 } 453 454 getMetaReplicaReadRequests(metaRegions, readReqsForMetaReplicas); 455 456 Configuration c = new Configuration(HTU.getConfiguration()); 457 c.setBoolean(HConstants.USE_META_REPLICAS, true); 458 c.set(LOCATOR_META_REPLICAS_MODE, "LoadBalance"); 459 try (Connection connection = ConnectionFactory.createConnection(c); 460 Table tableForGet = connection.getTable(tn); 461 RegionLocator locator = tableForGet.getRegionLocator()) { 462 byte[][] getRows = new byte[HBaseTestingUtil.KEYS.length][]; 463 for (int i = 1; i < HBaseTestingUtil.KEYS.length; i++) { 464 getRows[i] = HBaseTestingUtil.KEYS[i]; 465 } 466 getRows[0] = Bytes.toBytes("aaa"); 467 468 // There are more reads against all meta replica regions, including the primary region. 469 // Since in load balance mode, we will randomly select region replicas, it is possible that we 470 // missed read some replicas, so here we run it at most 3 times to make it more stable 471 runAtMostNTimes(3, () -> { 472 locator.clearRegionLocationCache(); 473 doNGets(tableForGet, getRows); 474 getMetaReplicaReadRequests(metaRegions, readReqsForMetaReplicasAfterGet); 475 return null; 476 }, () -> primaryIncreaseReplicaIncrease(readReqsForMetaReplicas, 477 readReqsForMetaReplicasAfterGet)); 478 479 // Same as above, we need to run it multiple times to make it stable. And numOfMetaReplicas is 480 // much less than HBaseTestingUtil.KEYS.length, so here we need to run more times. 481 runAtMostNTimes(30, () -> { 482 for (int i = 0; i < numOfMetaReplica; i++) { 483 locator.getAllRegionLocations(); 484 } 485 getMetaReplicaReadRequests(metaRegions, readReqsForMetaReplicasAfterGetAllLocations); 486 return null; 487 }, () -> primaryIncreaseReplicaIncrease(readReqsForMetaReplicasAfterGet, 488 readReqsForMetaReplicasAfterGetAllLocations)); 489 490 // move one of regions so it meta cache may be invalid. 491 HTU.moveRegionAndWait(userRegion.getRegionInfo(), destRs.getServerName()); 492 493 doNGets(tableForGet, getRows); 494 495 getMetaReplicaReadRequests(metaRegions, readReqsForMetaReplicasAfterMove); 496 497 // There are read requests increase for primary meta replica. 498 // For rest of meta replicas, there is no change as regionMove will tell the new location 499 primaryIncreaseReplicaNoChange(readReqsForMetaReplicasAfterGetAllLocations, 500 readReqsForMetaReplicasAfterMove); 501 // Move region again. 502 HTU.moveRegionAndWait(userRegion.getRegionInfo(), srcRs.getServerName()); 503 504 // Remove it from the move region cache 505 destRs.removeFromMovedRegions(userRegion.getRegionInfo().getEncodedName()); 506 507 getMetaReplicaReadRequests(metaRegions, readReqsForMetaReplicasAfterSecondMove); 508 509 // There are read requests increase for primary meta replica. 510 // For rest of meta replicas, there is no change. 511 primaryIncreaseReplicaNoChange(readReqsForMetaReplicasAfterMove, 512 readReqsForMetaReplicasAfterSecondMove); 513 514 doNGets(tableForGet, getRows); 515 516 getMetaReplicaReadRequests(metaRegions, readReqsForMetaReplicasAfterThirdGet); 517 518 // Since it gets RegionNotServedException, it will go to primary for the next lookup. 519 // There are read requests increase for primary meta replica. 520 // For rest of meta replicas, there is no change. 521 primaryIncreaseReplicaNoChange(readReqsForMetaReplicasAfterSecondMove, 522 readReqsForMetaReplicasAfterThirdGet); 523 } 524 } 525}