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