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