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}