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.master.assignment; 019 020import static org.apache.hadoop.hbase.master.assignment.AssignmentTestingUtil.insertData; 021import static org.junit.Assert.assertEquals; 022import static org.junit.Assert.assertFalse; 023import static org.junit.Assert.assertTrue; 024import static org.junit.Assert.fail; 025 026import java.io.IOException; 027import java.util.List; 028import java.util.Optional; 029import org.apache.hadoop.conf.Configuration; 030import org.apache.hadoop.hbase.Cell; 031import org.apache.hadoop.hbase.CellUtil; 032import org.apache.hadoop.hbase.DoNotRetryIOException; 033import org.apache.hadoop.hbase.HBaseClassTestRule; 034import org.apache.hadoop.hbase.HBaseTestingUtil; 035import org.apache.hadoop.hbase.HConstants; 036import org.apache.hadoop.hbase.TableName; 037import org.apache.hadoop.hbase.Waiter; 038import org.apache.hadoop.hbase.client.CompactionState; 039import org.apache.hadoop.hbase.client.Delete; 040import org.apache.hadoop.hbase.client.Get; 041import org.apache.hadoop.hbase.client.RegionInfo; 042import org.apache.hadoop.hbase.client.RegionReplicaUtil; 043import org.apache.hadoop.hbase.client.Result; 044import org.apache.hadoop.hbase.client.SnapshotDescription; 045import org.apache.hadoop.hbase.client.SnapshotType; 046import org.apache.hadoop.hbase.client.Table; 047import org.apache.hadoop.hbase.client.TableDescriptor; 048import org.apache.hadoop.hbase.coprocessor.ObserverContext; 049import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor; 050import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; 051import org.apache.hadoop.hbase.coprocessor.RegionObserver; 052import org.apache.hadoop.hbase.master.RegionState; 053import org.apache.hadoop.hbase.master.procedure.MasterProcedureConstants; 054import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; 055import org.apache.hadoop.hbase.master.procedure.MasterProcedureTestingUtility; 056import org.apache.hadoop.hbase.master.procedure.TestSnapshotProcedure; 057import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; 058import org.apache.hadoop.hbase.procedure2.ProcedureMetrics; 059import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility; 060import org.apache.hadoop.hbase.regionserver.HRegion; 061import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils; 062import org.apache.hadoop.hbase.testclassification.MasterTests; 063import org.apache.hadoop.hbase.testclassification.MediumTests; 064import org.apache.hadoop.hbase.util.Bytes; 065import org.junit.After; 066import org.junit.AfterClass; 067import org.junit.Before; 068import org.junit.BeforeClass; 069import org.junit.ClassRule; 070import org.junit.Rule; 071import org.junit.Test; 072import org.junit.experimental.categories.Category; 073import org.junit.rules.TestName; 074import org.slf4j.Logger; 075import org.slf4j.LoggerFactory; 076 077import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 078import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos; 079 080@Category({ MasterTests.class, MediumTests.class }) 081public class TestSplitTableRegionProcedure { 082 083 @ClassRule 084 public static final HBaseClassTestRule CLASS_RULE = 085 HBaseClassTestRule.forClass(TestSplitTableRegionProcedure.class); 086 087 private static final Logger LOG = LoggerFactory.getLogger(TestSplitTableRegionProcedure.class); 088 089 protected static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); 090 091 private static String columnFamilyName1 = "cf1"; 092 private static String columnFamilyName2 = "cf2"; 093 094 private static final int startRowNum = 11; 095 private static final int rowCount = 60; 096 097 private AssignmentManager am; 098 099 private ProcedureMetrics splitProcMetrics; 100 private ProcedureMetrics assignProcMetrics; 101 private ProcedureMetrics unassignProcMetrics; 102 103 private long splitSubmittedCount = 0; 104 private long splitFailedCount = 0; 105 private long assignSubmittedCount = 0; 106 private long assignFailedCount = 0; 107 private long unassignSubmittedCount = 0; 108 private long unassignFailedCount = 0; 109 110 @Rule 111 public TestName name = new TestName(); 112 113 private static void setupConf(Configuration conf) { 114 conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1); 115 conf.setLong(HConstants.MAJOR_COMPACTION_PERIOD, 0); 116 conf.set("hbase.coprocessor.region.classes", 117 RegionServerHostingReplicaSlowOpenCopro.class.getName()); 118 conf.setInt("hbase.client.sync.wait.timeout.msec", 1500); 119 } 120 121 /** 122 * This copro is used to slow down opening of the replica regions. 123 */ 124 public static class RegionServerHostingReplicaSlowOpenCopro 125 implements RegionCoprocessor, RegionObserver { 126 static int countForReplica = 0; 127 static boolean slowDownReplicaOpen = false; 128 129 @Override 130 public Optional<RegionObserver> getRegionObserver() { 131 return Optional.of(this); 132 } 133 134 @Override 135 public void preOpen(ObserverContext<? extends RegionCoprocessorEnvironment> c) 136 throws IOException { 137 int replicaId = c.getEnvironment().getRegion().getRegionInfo().getReplicaId(); 138 if ((replicaId != RegionInfo.DEFAULT_REPLICA_ID) && (countForReplica == 0)) { 139 countForReplica++; 140 while (slowDownReplicaOpen) { 141 LOG.info("Slow down replica region open a bit"); 142 try { 143 Thread.sleep(100); 144 } catch (InterruptedException ie) { 145 // Ingore 146 } 147 } 148 } 149 } 150 } 151 152 @BeforeClass 153 public static void setupCluster() throws Exception { 154 setupConf(UTIL.getConfiguration()); 155 UTIL.startMiniCluster(3); 156 } 157 158 @AfterClass 159 public static void cleanupTest() throws Exception { 160 try { 161 UTIL.shutdownMiniCluster(); 162 } catch (Exception e) { 163 LOG.warn("failure shutting down cluster", e); 164 } 165 } 166 167 @Before 168 public void setup() throws Exception { 169 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false); 170 171 // Turn off balancer so it doesn't cut in and mess up our placements. 172 UTIL.getAdmin().balancerSwitch(false, true); 173 // Turn off the meta scanner so it don't remove parent on us. 174 UTIL.getHBaseCluster().getMaster().setCatalogJanitorEnabled(false); 175 am = UTIL.getHBaseCluster().getMaster().getAssignmentManager(); 176 splitProcMetrics = am.getAssignmentManagerMetrics().getSplitProcMetrics(); 177 assignProcMetrics = am.getAssignmentManagerMetrics().getAssignProcMetrics(); 178 unassignProcMetrics = am.getAssignmentManagerMetrics().getUnassignProcMetrics(); 179 } 180 181 @After 182 public void tearDown() throws Exception { 183 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false); 184 for (TableDescriptor htd : UTIL.getAdmin().listTableDescriptors()) { 185 UTIL.deleteTable(htd.getTableName()); 186 } 187 } 188 189 @Test 190 public void testRollbackForSplitTableRegionWithReplica() throws Exception { 191 final TableName tableName = TableName.valueOf(name.getMethodName()); 192 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 193 194 RegionServerHostingReplicaSlowOpenCopro.slowDownReplicaOpen = true; 195 RegionInfo[] regions = 196 MasterProcedureTestingUtility.createTable(procExec, tableName, null, columnFamilyName1); 197 198 try { 199 HBaseTestingUtil.setReplicas(UTIL.getAdmin(), tableName, 2); 200 } catch (IOException ioe) { 201 202 } 203 204 // wait until the primary region is online. 205 HBaseTestingUtil.await(2000, () -> { 206 try { 207 AssignmentManager am = UTIL.getHBaseCluster().getMaster().getAssignmentManager(); 208 if (am == null) return false; 209 if (am.getRegionStates().getRegionState(regions[0]).isOpened()) { 210 return true; 211 } 212 return false; 213 } catch (Exception e) { 214 throw new RuntimeException(e); 215 } 216 }); 217 218 // Split region of the table, it will fail and rollback as replica parent region 219 // is still at OPENING state. 220 long procId = procExec.submitProcedure(new SplitTableRegionProcedure(procExec.getEnvironment(), 221 regions[0], HConstants.CATALOG_FAMILY)); 222 // Wait for the completion. 223 ProcedureTestingUtility.waitProcedure(procExec, procId); 224 225 // Let replica parent region open. 226 RegionServerHostingReplicaSlowOpenCopro.slowDownReplicaOpen = false; 227 228 // wait until the replica region is online. 229 HBaseTestingUtil.await(2000, () -> { 230 try { 231 AssignmentManager am = UTIL.getHBaseCluster().getMaster().getAssignmentManager(); 232 if (am == null) return false; 233 RegionInfo replicaRegion = RegionReplicaUtil.getRegionInfoForReplica(regions[0], 1); 234 if (am.getRegionStates().getRegionState(replicaRegion).isOpened()) { 235 return true; 236 } 237 return false; 238 } catch (Exception e) { 239 throw new RuntimeException(e); 240 } 241 }); 242 243 ProcedureTestingUtility.assertProcFailed(procExec, procId); 244 // There should not be any active OpenRegionProcedure 245 procExec.getActiveProceduresNoCopy() 246 .forEach(p -> assertFalse(p instanceof OpenRegionProcedure)); 247 248 // Check that procedure rollback reverted parent region state to OPEN 249 AssignmentManager am = UTIL.getHBaseCluster().getMaster().getAssignmentManager(); 250 RegionStateNode regionStateNode = am.getRegionStates().getRegionStateNode(regions[0]); 251 assertEquals(RegionState.State.OPEN, regionStateNode.getState()); 252 } 253 254 @Test 255 public void testSplitTableRegion() throws Exception { 256 final TableName tableName = TableName.valueOf(name.getMethodName()); 257 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 258 259 RegionInfo[] regions = MasterProcedureTestingUtility.createTable(procExec, tableName, null, 260 columnFamilyName1, columnFamilyName2); 261 insertData(UTIL, tableName, rowCount, startRowNum, columnFamilyName1, columnFamilyName2); 262 int splitRowNum = startRowNum + rowCount / 2; 263 byte[] splitKey = Bytes.toBytes("" + splitRowNum); 264 265 assertTrue("not able to find a splittable region", regions != null); 266 assertTrue("not able to find a splittable region", regions.length == 1); 267 268 // collect AM metrics before test 269 collectAssignmentManagerMetrics(); 270 271 // Split region of the table 272 long procId = procExec.submitProcedure( 273 new SplitTableRegionProcedure(procExec.getEnvironment(), regions[0], splitKey)); 274 // Wait the completion 275 ProcedureTestingUtility.waitProcedure(procExec, procId); 276 ProcedureTestingUtility.assertProcNotFailed(procExec, procId); 277 278 verify(tableName, splitRowNum); 279 280 assertEquals(splitSubmittedCount + 1, splitProcMetrics.getSubmittedCounter().getCount()); 281 assertEquals(splitFailedCount, splitProcMetrics.getFailedCounter().getCount()); 282 assertEquals(assignSubmittedCount + 2, assignProcMetrics.getSubmittedCounter().getCount()); 283 assertEquals(assignFailedCount, assignProcMetrics.getFailedCounter().getCount()); 284 assertEquals(unassignSubmittedCount + 1, unassignProcMetrics.getSubmittedCounter().getCount()); 285 assertEquals(unassignFailedCount, unassignProcMetrics.getFailedCounter().getCount()); 286 } 287 288 @Test 289 public void testSplitTableRegionNoStoreFile() throws Exception { 290 final TableName tableName = TableName.valueOf(name.getMethodName()); 291 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 292 293 RegionInfo[] regions = MasterProcedureTestingUtility.createTable(procExec, tableName, null, 294 columnFamilyName1, columnFamilyName2); 295 int splitRowNum = startRowNum + rowCount / 2; 296 byte[] splitKey = Bytes.toBytes("" + splitRowNum); 297 298 assertTrue("not able to find a splittable region", regions != null); 299 assertTrue("not able to find a splittable region", regions.length == 1); 300 301 // collect AM metrics before test 302 collectAssignmentManagerMetrics(); 303 304 // Split region of the table 305 long procId = procExec.submitProcedure( 306 new SplitTableRegionProcedure(procExec.getEnvironment(), regions[0], splitKey)); 307 // Wait the completion 308 ProcedureTestingUtility.waitProcedure(procExec, procId); 309 ProcedureTestingUtility.assertProcNotFailed(procExec, procId); 310 311 assertTrue(UTIL.getMiniHBaseCluster().getRegions(tableName).size() == 2); 312 assertTrue(UTIL.countRows(tableName) == 0); 313 314 assertEquals(splitSubmittedCount + 1, splitProcMetrics.getSubmittedCounter().getCount()); 315 assertEquals(splitFailedCount, splitProcMetrics.getFailedCounter().getCount()); 316 } 317 318 @Test 319 public void testSplitTableRegionUnevenDaughter() throws Exception { 320 final TableName tableName = TableName.valueOf(name.getMethodName()); 321 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 322 323 RegionInfo[] regions = MasterProcedureTestingUtility.createTable(procExec, tableName, null, 324 columnFamilyName1, columnFamilyName2); 325 insertData(UTIL, tableName, rowCount, startRowNum, columnFamilyName1, columnFamilyName2); 326 // Split to two daughters with one of them only has 1 row 327 int splitRowNum = startRowNum + rowCount / 4; 328 byte[] splitKey = Bytes.toBytes("" + splitRowNum); 329 330 assertTrue("not able to find a splittable region", regions != null); 331 assertTrue("not able to find a splittable region", regions.length == 1); 332 333 // collect AM metrics before test 334 collectAssignmentManagerMetrics(); 335 336 // Split region of the table 337 long procId = procExec.submitProcedure( 338 new SplitTableRegionProcedure(procExec.getEnvironment(), regions[0], splitKey)); 339 // Wait the completion 340 ProcedureTestingUtility.waitProcedure(procExec, procId); 341 ProcedureTestingUtility.assertProcNotFailed(procExec, procId); 342 343 verify(tableName, splitRowNum); 344 345 assertEquals(splitSubmittedCount + 1, splitProcMetrics.getSubmittedCounter().getCount()); 346 assertEquals(splitFailedCount, splitProcMetrics.getFailedCounter().getCount()); 347 } 348 349 @Test 350 public void testSplitTableRegionEmptyDaughter() throws Exception { 351 final TableName tableName = TableName.valueOf(name.getMethodName()); 352 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 353 354 RegionInfo[] regions = MasterProcedureTestingUtility.createTable(procExec, tableName, null, 355 columnFamilyName1, columnFamilyName2); 356 insertData(UTIL, tableName, rowCount, startRowNum, columnFamilyName1, columnFamilyName2); 357 // Split to two daughters with one of them only has 1 row 358 int splitRowNum = startRowNum + rowCount; 359 byte[] splitKey = Bytes.toBytes("" + splitRowNum); 360 361 assertTrue("not able to find a splittable region", regions != null); 362 assertTrue("not able to find a splittable region", regions.length == 1); 363 364 // collect AM metrics before test 365 collectAssignmentManagerMetrics(); 366 367 // Split region of the table 368 long procId = procExec.submitProcedure( 369 new SplitTableRegionProcedure(procExec.getEnvironment(), regions[0], splitKey)); 370 // Wait the completion 371 ProcedureTestingUtility.waitProcedure(procExec, procId); 372 ProcedureTestingUtility.assertProcNotFailed(procExec, procId); 373 374 // Make sure one daughter has 0 rows. 375 List<HRegion> daughters = UTIL.getMiniHBaseCluster().getRegions(tableName); 376 assertTrue(daughters.size() == 2); 377 assertTrue(UTIL.countRows(tableName) == rowCount); 378 assertTrue(UTIL.countRows(daughters.get(0)) == 0 || UTIL.countRows(daughters.get(1)) == 0); 379 380 assertEquals(splitSubmittedCount + 1, splitProcMetrics.getSubmittedCounter().getCount()); 381 assertEquals(splitFailedCount, splitProcMetrics.getFailedCounter().getCount()); 382 } 383 384 @Test 385 public void testSplitTableRegionDeletedRowsDaughter() throws Exception { 386 final TableName tableName = TableName.valueOf(name.getMethodName()); 387 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 388 389 RegionInfo[] regions = MasterProcedureTestingUtility.createTable(procExec, tableName, null, 390 columnFamilyName1, columnFamilyName2); 391 insertData(UTIL, tableName, rowCount, startRowNum, columnFamilyName1, columnFamilyName2); 392 // Split to two daughters with one of them only has 1 row 393 int splitRowNum = rowCount; 394 deleteData(tableName, splitRowNum); 395 byte[] splitKey = Bytes.toBytes("" + splitRowNum); 396 397 assertTrue("not able to find a splittable region", regions != null); 398 assertTrue("not able to find a splittable region", regions.length == 1); 399 400 // collect AM metrics before test 401 collectAssignmentManagerMetrics(); 402 403 // Split region of the table 404 long procId = procExec.submitProcedure( 405 new SplitTableRegionProcedure(procExec.getEnvironment(), regions[0], splitKey)); 406 // Wait the completion 407 ProcedureTestingUtility.waitProcedure(procExec, procId); 408 ProcedureTestingUtility.assertProcNotFailed(procExec, procId); 409 410 UTIL.getAdmin().majorCompact(tableName); 411 // waiting for the major compaction to complete 412 UTIL.waitFor(6000, new Waiter.Predicate<IOException>() { 413 @Override 414 public boolean evaluate() throws IOException { 415 return UTIL.getAdmin().getCompactionState(tableName) == CompactionState.NONE; 416 } 417 }); 418 419 // Make sure one daughter has 0 rows. 420 List<HRegion> daughters = UTIL.getMiniHBaseCluster().getRegions(tableName); 421 assertTrue(daughters.size() == 2); 422 final int currentRowCount = splitRowNum - startRowNum; 423 assertTrue(UTIL.countRows(tableName) == currentRowCount); 424 assertTrue(UTIL.countRows(daughters.get(0)) == 0 || UTIL.countRows(daughters.get(1)) == 0); 425 426 assertEquals(splitSubmittedCount + 1, splitProcMetrics.getSubmittedCounter().getCount()); 427 assertEquals(splitFailedCount, splitProcMetrics.getFailedCounter().getCount()); 428 } 429 430 @Test 431 public void testInvalidSplitKey() throws Exception { 432 final TableName tableName = TableName.valueOf(name.getMethodName()); 433 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 434 435 RegionInfo[] regions = MasterProcedureTestingUtility.createTable(procExec, tableName, null, 436 columnFamilyName1, columnFamilyName2); 437 insertData(UTIL, tableName, rowCount, startRowNum, columnFamilyName1, columnFamilyName2); 438 439 assertTrue("not able to find a splittable region", regions != null); 440 assertTrue("not able to find a splittable region", regions.length == 1); 441 442 // collect AM metrics before test 443 collectAssignmentManagerMetrics(); 444 445 // Split region of the table with null split key 446 try { 447 long procId1 = procExec.submitProcedure( 448 new SplitTableRegionProcedure(procExec.getEnvironment(), regions[0], null)); 449 ProcedureTestingUtility.waitProcedure(procExec, procId1); 450 fail("unexpected procedure start with invalid split-key"); 451 } catch (DoNotRetryIOException e) { 452 LOG.debug("Expected Split procedure construction failure: " + e.getMessage()); 453 } 454 455 assertEquals(splitSubmittedCount, splitProcMetrics.getSubmittedCounter().getCount()); 456 assertEquals(splitFailedCount, splitProcMetrics.getFailedCounter().getCount()); 457 } 458 459 @Test 460 public void testRollbackAndDoubleExecution() throws Exception { 461 final TableName tableName = TableName.valueOf(name.getMethodName()); 462 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 463 464 RegionInfo[] regions = MasterProcedureTestingUtility.createTable(procExec, tableName, null, 465 columnFamilyName1, columnFamilyName2); 466 insertData(UTIL, tableName, rowCount, startRowNum, columnFamilyName1, columnFamilyName2); 467 int splitRowNum = startRowNum + rowCount / 2; 468 byte[] splitKey = Bytes.toBytes("" + splitRowNum); 469 470 assertTrue("not able to find a splittable region", regions != null); 471 assertTrue("not able to find a splittable region", regions.length == 1); 472 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 473 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 474 475 // collect AM metrics before test 476 collectAssignmentManagerMetrics(); 477 478 // Split region of the table 479 long procId = procExec.submitProcedure( 480 new SplitTableRegionProcedure(procExec.getEnvironment(), regions[0], splitKey)); 481 482 // Failing before SPLIT_TABLE_REGION_UPDATE_META we should trigger the 483 // rollback 484 // NOTE: the 7 (number of SPLIT_TABLE_REGION_UPDATE_META step) is 485 // hardcoded, so you have to look at this test at least once when you add a new step. 486 int lastStep = 7; 487 MasterProcedureTestingUtility.testRollbackAndDoubleExecution(procExec, procId, lastStep, true); 488 // check that we have only 1 region 489 assertEquals(1, UTIL.getAdmin().getRegions(tableName).size()); 490 UTIL.waitUntilAllRegionsAssigned(tableName); 491 List<HRegion> newRegions = UTIL.getMiniHBaseCluster().getRegions(tableName); 492 assertEquals(1, newRegions.size()); 493 verifyData(newRegions.get(0), startRowNum, rowCount, Bytes.toBytes(columnFamilyName1), 494 Bytes.toBytes(columnFamilyName2)); 495 496 assertEquals(splitSubmittedCount + 1, splitProcMetrics.getSubmittedCounter().getCount()); 497 assertEquals(splitFailedCount + 1, splitProcMetrics.getFailedCounter().getCount()); 498 } 499 500 @Test 501 public void testRecoveryAndDoubleExecution() throws Exception { 502 final TableName tableName = TableName.valueOf(name.getMethodName()); 503 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 504 505 RegionInfo[] regions = MasterProcedureTestingUtility.createTable(procExec, tableName, null, 506 columnFamilyName1, columnFamilyName2); 507 insertData(UTIL, tableName, rowCount, startRowNum, columnFamilyName1, columnFamilyName2); 508 int splitRowNum = startRowNum + rowCount / 2; 509 byte[] splitKey = Bytes.toBytes("" + splitRowNum); 510 511 assertTrue("not able to find a splittable region", regions != null); 512 assertTrue("not able to find a splittable region", regions.length == 1); 513 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 514 ProcedureTestingUtility.setKillIfHasParent(procExec, false); 515 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 516 517 // collect AM metrics before test 518 collectAssignmentManagerMetrics(); 519 520 // Split region of the table 521 long procId = procExec.submitProcedure( 522 new SplitTableRegionProcedure(procExec.getEnvironment(), regions[0], splitKey)); 523 524 // Restart the executor and execute the step twice 525 MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(procExec, procId); 526 ProcedureTestingUtility.assertProcNotFailed(procExec, procId); 527 528 verify(tableName, splitRowNum); 529 530 assertEquals(splitSubmittedCount + 1, splitProcMetrics.getSubmittedCounter().getCount()); 531 assertEquals(splitFailedCount, splitProcMetrics.getFailedCounter().getCount()); 532 } 533 534 @Test 535 public void testSplitWithoutPONR() throws Exception { 536 final TableName tableName = TableName.valueOf(name.getMethodName()); 537 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 538 539 RegionInfo[] regions = MasterProcedureTestingUtility.createTable(procExec, tableName, null, 540 columnFamilyName1, columnFamilyName2); 541 insertData(UTIL, tableName, rowCount, startRowNum, columnFamilyName1, columnFamilyName2); 542 int splitRowNum = startRowNum + rowCount / 2; 543 byte[] splitKey = Bytes.toBytes("" + splitRowNum); 544 545 assertTrue("not able to find a splittable region", regions != null); 546 assertTrue("not able to find a splittable region", regions.length == 1); 547 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 548 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 549 550 // Split region of the table 551 long procId = procExec.submitProcedure( 552 new SplitTableRegionProcedure(procExec.getEnvironment(), regions[0], splitKey)); 553 554 // Execute until step 7 of split procedure 555 // NOTE: the 7 (number after SPLIT_TABLE_REGION_UPDATE_META step) 556 MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(procExec, procId, 7, false); 557 558 // Unset Toggle Kill and make ProcExec work correctly 559 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false); 560 MasterProcedureTestingUtility.restartMasterProcedureExecutor(procExec); 561 ProcedureTestingUtility.waitProcedure(procExec, procId); 562 563 // Even split failed after step 4, it should still works fine 564 verify(tableName, splitRowNum); 565 } 566 567 @Test 568 public void testSplitRegionWhileTakingSnapshot() throws Exception { 569 final TableName tableName = TableName.valueOf(name.getMethodName()); 570 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 571 572 RegionInfo[] regions = MasterProcedureTestingUtility.createTable(procExec, tableName, null, 573 columnFamilyName1, columnFamilyName2); 574 int splitRowNum = startRowNum + rowCount / 2; 575 byte[] splitKey = Bytes.toBytes("" + splitRowNum); 576 577 assertTrue("not able to find a splittable region", regions != null); 578 assertTrue("not able to find a splittable region", regions.length == 1); 579 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 580 581 // task snapshot 582 SnapshotDescription snapshot = 583 new SnapshotDescription("SnapshotProcedureTest", tableName, SnapshotType.FLUSH); 584 SnapshotProtos.SnapshotDescription snapshotProto = 585 ProtobufUtil.createHBaseProtosSnapshotDesc(snapshot); 586 snapshotProto = SnapshotDescriptionUtils.validate(snapshotProto, 587 UTIL.getHBaseCluster().getMaster().getConfiguration()); 588 long snapshotProcId = procExec.submitProcedure( 589 new TestSnapshotProcedure.DelaySnapshotProcedure(procExec.getEnvironment(), snapshotProto)); 590 UTIL.getHBaseCluster().getMaster().getSnapshotManager().registerSnapshotProcedure(snapshotProto, 591 snapshotProcId); 592 593 // collect AM metrics before test 594 collectAssignmentManagerMetrics(); 595 596 // Split region of the table 597 long procId = procExec.submitProcedure( 598 new SplitTableRegionProcedure(procExec.getEnvironment(), regions[0], splitKey)); 599 // Wait the completion 600 ProcedureTestingUtility.waitProcedure(procExec, procId); 601 ProcedureTestingUtility.waitProcedure(procExec, snapshotProcId); 602 603 ProcedureTestingUtility.assertProcFailed(procExec, procId); 604 ProcedureTestingUtility.assertProcNotFailed(procExec, snapshotProcId); 605 606 assertTrue(UTIL.getMiniHBaseCluster().getRegions(tableName).size() == 1); 607 assertTrue(UTIL.countRows(tableName) == 0); 608 609 assertEquals(splitSubmittedCount + 1, splitProcMetrics.getSubmittedCounter().getCount()); 610 assertEquals(splitFailedCount + 1, splitProcMetrics.getFailedCounter().getCount()); 611 } 612 613 private void deleteData(final TableName tableName, final int startDeleteRowNum) 614 throws IOException, InterruptedException { 615 Table t = UTIL.getConnection().getTable(tableName); 616 final int numRows = rowCount + startRowNum - startDeleteRowNum; 617 Delete d; 618 for (int i = startDeleteRowNum; i <= numRows + startDeleteRowNum; i++) { 619 d = new Delete(Bytes.toBytes("" + i)); 620 t.delete(d); 621 if (i % 5 == 0) { 622 UTIL.getAdmin().flush(tableName); 623 } 624 } 625 } 626 627 private void verify(final TableName tableName, final int splitRowNum) throws IOException { 628 List<HRegion> daughters = UTIL.getMiniHBaseCluster().getRegions(tableName); 629 assertTrue(daughters.size() == 2); 630 LOG.info("Row Count = " + UTIL.countRows(tableName)); 631 assertTrue(UTIL.countRows(tableName) == rowCount); 632 int startRow; 633 int numRows; 634 for (int i = 0; i < daughters.size(); i++) { 635 if ( 636 Bytes.compareTo(daughters.get(i).getRegionInfo().getStartKey(), HConstants.EMPTY_BYTE_ARRAY) 637 == 0 638 ) { 639 startRow = startRowNum; // first region 640 numRows = splitRowNum - startRowNum; 641 } else { 642 startRow = splitRowNum; 643 numRows = rowCount + startRowNum - splitRowNum; 644 } 645 verifyData(daughters.get(i), startRow, numRows, Bytes.toBytes(columnFamilyName1), 646 Bytes.toBytes(columnFamilyName2)); 647 } 648 } 649 650 private void verifyData(final HRegion newReg, final int startRow, final int numRows, 651 final byte[]... families) throws IOException { 652 for (int i = startRow; i < startRow + numRows; i++) { 653 byte[] row = Bytes.toBytes("" + i); 654 Get get = new Get(row); 655 Result result = newReg.get(get); 656 Cell[] raw = result.rawCells(); 657 assertEquals(families.length, result.size()); 658 for (int j = 0; j < families.length; j++) { 659 assertTrue(CellUtil.matchingRows(raw[j], row)); 660 assertTrue(CellUtil.matchingFamily(raw[j], families[j])); 661 } 662 } 663 } 664 665 private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() { 666 return UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor(); 667 } 668 669 private void collectAssignmentManagerMetrics() { 670 splitSubmittedCount = splitProcMetrics.getSubmittedCounter().getCount(); 671 splitFailedCount = splitProcMetrics.getFailedCounter().getCount(); 672 assignSubmittedCount = assignProcMetrics.getSubmittedCounter().getCount(); 673 assignFailedCount = assignProcMetrics.getFailedCounter().getCount(); 674 unassignSubmittedCount = unassignProcMetrics.getSubmittedCounter().getCount(); 675 unassignFailedCount = unassignProcMetrics.getFailedCounter().getCount(); 676 } 677}