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.regionserver;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertNotEquals;
023import static org.junit.Assert.assertNotNull;
024import static org.junit.Assert.assertNotSame;
025import static org.junit.Assert.assertNull;
026import static org.junit.Assert.assertTrue;
027import static org.junit.Assert.fail;
028
029import java.io.IOException;
030import java.lang.reflect.Field;
031import java.util.ArrayList;
032import java.util.Collection;
033import java.util.List;
034import java.util.Map;
035import java.util.Optional;
036import java.util.concurrent.CountDownLatch;
037import java.util.concurrent.ExecutionException;
038import java.util.concurrent.TimeUnit;
039import java.util.concurrent.TimeoutException;
040import java.util.concurrent.atomic.AtomicBoolean;
041import org.apache.commons.io.IOUtils;
042import org.apache.hadoop.conf.Configuration;
043import org.apache.hadoop.fs.FileSystem;
044import org.apache.hadoop.fs.Path;
045import org.apache.hadoop.hbase.Coprocessor;
046import org.apache.hadoop.hbase.CoprocessorEnvironment;
047import org.apache.hadoop.hbase.DoNotRetryIOException;
048import org.apache.hadoop.hbase.HBaseClassTestRule;
049import org.apache.hadoop.hbase.HBaseTestingUtility;
050import org.apache.hadoop.hbase.HConstants;
051import org.apache.hadoop.hbase.HTableDescriptor;
052import org.apache.hadoop.hbase.MasterNotRunningException;
053import org.apache.hadoop.hbase.MiniHBaseCluster;
054import org.apache.hadoop.hbase.ServerName;
055import org.apache.hadoop.hbase.StartMiniClusterOption;
056import org.apache.hadoop.hbase.TableName;
057import org.apache.hadoop.hbase.UnknownRegionException;
058import org.apache.hadoop.hbase.ZooKeeperConnectionException;
059import org.apache.hadoop.hbase.client.Admin;
060import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
061import org.apache.hadoop.hbase.client.Consistency;
062import org.apache.hadoop.hbase.client.Delete;
063import org.apache.hadoop.hbase.client.DoNotRetryRegionException;
064import org.apache.hadoop.hbase.client.Get;
065import org.apache.hadoop.hbase.client.Mutation;
066import org.apache.hadoop.hbase.client.Put;
067import org.apache.hadoop.hbase.client.RegionInfo;
068import org.apache.hadoop.hbase.client.Result;
069import org.apache.hadoop.hbase.client.ResultScanner;
070import org.apache.hadoop.hbase.client.Scan;
071import org.apache.hadoop.hbase.client.Table;
072import org.apache.hadoop.hbase.client.TableDescriptor;
073import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
074import org.apache.hadoop.hbase.client.TestReplicasClient.SlowMeCopro;
075import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor;
076import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
077import org.apache.hadoop.hbase.coprocessor.MasterObserver;
078import org.apache.hadoop.hbase.coprocessor.ObserverContext;
079import org.apache.hadoop.hbase.io.Reference;
080import org.apache.hadoop.hbase.master.HMaster;
081import org.apache.hadoop.hbase.master.LoadBalancer;
082import org.apache.hadoop.hbase.master.MasterRpcServices;
083import org.apache.hadoop.hbase.master.RegionState;
084import org.apache.hadoop.hbase.master.RegionState.State;
085import org.apache.hadoop.hbase.master.assignment.AssignmentManager;
086import org.apache.hadoop.hbase.master.assignment.AssignmentTestingUtil;
087import org.apache.hadoop.hbase.master.assignment.RegionStateNode;
088import org.apache.hadoop.hbase.master.assignment.RegionStates;
089import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
090import org.apache.hadoop.hbase.regionserver.compactions.CompactionContext;
091import org.apache.hadoop.hbase.regionserver.compactions.CompactionLifeCycleTracker;
092import org.apache.hadoop.hbase.regionserver.compactions.CompactionProgress;
093import org.apache.hadoop.hbase.regionserver.throttle.NoLimitThroughputController;
094import org.apache.hadoop.hbase.testclassification.LargeTests;
095import org.apache.hadoop.hbase.testclassification.RegionServerTests;
096import org.apache.hadoop.hbase.util.Bytes;
097import org.apache.hadoop.hbase.util.CommonFSUtils;
098import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
099import org.apache.hadoop.hbase.util.FSUtils;
100import org.apache.hadoop.hbase.util.HBaseFsck;
101import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;
102import org.apache.hadoop.hbase.util.Threads;
103import org.apache.zookeeper.KeeperException;
104import org.apache.zookeeper.KeeperException.NodeExistsException;
105import org.junit.After;
106import org.junit.AfterClass;
107import org.junit.Assert;
108import org.junit.Before;
109import org.junit.BeforeClass;
110import org.junit.ClassRule;
111import org.junit.Rule;
112import org.junit.Test;
113import org.junit.experimental.categories.Category;
114import org.junit.rules.TestName;
115import org.mockito.Mockito;
116import org.slf4j.Logger;
117import org.slf4j.LoggerFactory;
118
119import org.apache.hbase.thirdparty.com.google.protobuf.RpcController;
120import org.apache.hbase.thirdparty.com.google.protobuf.ServiceException;
121
122import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
123import org.apache.hadoop.hbase.shaded.protobuf.generated.RegionServerStatusProtos.RegionStateTransition.TransitionCode;
124import org.apache.hadoop.hbase.shaded.protobuf.generated.RegionServerStatusProtos.ReportRegionStateTransitionRequest;
125import org.apache.hadoop.hbase.shaded.protobuf.generated.RegionServerStatusProtos.ReportRegionStateTransitionResponse;
126
127/**
128 * The below tests are testing split region against a running cluster
129 */
130@Category({RegionServerTests.class, LargeTests.class})
131public class TestSplitTransactionOnCluster {
132
133  @ClassRule
134  public static final HBaseClassTestRule CLASS_RULE =
135      HBaseClassTestRule.forClass(TestSplitTransactionOnCluster.class);
136
137  private static final Logger LOG = LoggerFactory.getLogger(TestSplitTransactionOnCluster.class);
138  private Admin admin = null;
139  private MiniHBaseCluster cluster = null;
140  private static final int NB_SERVERS = 3;
141
142  static final HBaseTestingUtility TESTING_UTIL =
143    new HBaseTestingUtility();
144
145  @Rule
146  public TestName name = new TestName();
147
148  @BeforeClass public static void before() throws Exception {
149    TESTING_UTIL.getConfiguration().setInt(HConstants.HBASE_BALANCER_PERIOD, 60000);
150    StartMiniClusterOption option = StartMiniClusterOption.builder()
151        .masterClass(MyMaster.class).numRegionServers(NB_SERVERS).
152            numDataNodes(NB_SERVERS).build();
153    TESTING_UTIL.startMiniCluster(option);
154  }
155
156  @AfterClass public static void after() throws Exception {
157    TESTING_UTIL.shutdownMiniCluster();
158  }
159
160  @Before public void setup() throws IOException {
161    TESTING_UTIL.ensureSomeNonStoppedRegionServersAvailable(NB_SERVERS);
162    this.admin = TESTING_UTIL.getAdmin();
163    this.cluster = TESTING_UTIL.getMiniHBaseCluster();
164  }
165
166  @After
167  public void tearDown() throws Exception {
168    this.admin.close();
169    for (TableDescriptor htd: this.admin.listTableDescriptors()) {
170      LOG.info("Tear down, remove table=" + htd.getTableName());
171      TESTING_UTIL.deleteTable(htd.getTableName());
172    }
173  }
174
175  private RegionInfo getAndCheckSingleTableRegion(final List<HRegion> regions)
176      throws IOException, InterruptedException {
177    assertEquals(1, regions.size());
178    RegionInfo hri = regions.get(0).getRegionInfo();
179    AssignmentTestingUtil.waitForAssignment(cluster.getMaster().getAssignmentManager(), hri);
180    return hri;
181  }
182
183  private void requestSplitRegion(
184      final HRegionServer rsServer,
185      final Region region,
186      final byte[] midKey) throws IOException {
187    long procId = cluster.getMaster().splitRegion(region.getRegionInfo(), midKey, 0, 0);
188    // wait for the split to complete or get interrupted.  If the split completes successfully,
189    // the procedure will return true; if the split fails, the procedure would throw exception.
190    ProcedureTestingUtility.waitProcedure(cluster.getMaster().getMasterProcedureExecutor(), procId);
191  }
192
193  @Test
194  public void testRITStateForRollback() throws Exception {
195    final TableName tableName = TableName.valueOf(name.getMethodName());
196    final HMaster master = cluster.getMaster();
197    try {
198      // Create table then get the single region for our new table.
199      Table t = createTableAndWait(tableName, Bytes.toBytes("cf"));
200      final List<HRegion> regions = cluster.getRegions(tableName);
201      final RegionInfo hri = getAndCheckSingleTableRegion(regions);
202      insertData(tableName, admin, t);
203      t.close();
204
205      // Turn off balancer so it doesn't cut in and mess up our placements.
206      this.admin.balancerSwitch(false, true);
207      // Turn off the meta scanner so it don't remove parent on us.
208      master.setCatalogJanitorEnabled(false);
209
210      // find a splittable region
211      final HRegion region = findSplittableRegion(regions);
212      assertTrue("not able to find a splittable region", region != null);
213
214      // install master co-processor to fail splits
215      master.getMasterCoprocessorHost().load(
216        FailingSplitMasterObserver.class,
217        Coprocessor.PRIORITY_USER,
218        master.getConfiguration());
219
220      // split async
221      this.admin.splitRegionAsync(region.getRegionInfo().getRegionName(), new byte[] { 42 });
222
223      // we have to wait until the SPLITTING state is seen by the master
224      FailingSplitMasterObserver observer =
225          master.getMasterCoprocessorHost().findCoprocessor(FailingSplitMasterObserver.class);
226      assertNotNull(observer);
227      observer.latch.await();
228
229      LOG.info("Waiting for region to come out of RIT");
230      while (!cluster.getMaster().getAssignmentManager().getRegionStates().isRegionOnline(hri)) {
231        Threads.sleep(100);
232      }
233      assertTrue(cluster.getMaster().getAssignmentManager().getRegionStates().isRegionOnline(hri));
234    } finally {
235      admin.balancerSwitch(true, false);
236      master.setCatalogJanitorEnabled(true);
237      abortAndWaitForMaster();
238      TESTING_UTIL.deleteTable(tableName);
239    }
240  }
241
242  @Test
243  public void testSplitFailedCompactionAndSplit() throws Exception {
244    final TableName tableName = TableName.valueOf(name.getMethodName());
245    // Create table then get the single region for our new table.
246    byte[] cf = Bytes.toBytes("cf");
247    TableDescriptor htd = TableDescriptorBuilder.newBuilder(tableName)
248      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(cf)).build();
249    admin.createTable(htd);
250
251    for (int i = 0; cluster.getRegions(tableName).isEmpty() && i < 100; i++) {
252      Thread.sleep(100);
253    }
254    assertEquals(1, cluster.getRegions(tableName).size());
255
256    HRegion region = cluster.getRegions(tableName).get(0);
257    HStore store = region.getStore(cf);
258    int regionServerIndex = cluster.getServerWith(region.getRegionInfo().getRegionName());
259    HRegionServer regionServer = cluster.getRegionServer(regionServerIndex);
260
261    Table t = TESTING_UTIL.getConnection().getTable(tableName);
262    // insert data
263    insertData(tableName, admin, t);
264    insertData(tableName, admin, t);
265
266    int fileNum = store.getStorefiles().size();
267    // 0, Compaction Request
268    store.triggerMajorCompaction();
269    Optional<CompactionContext> cc = store.requestCompaction();
270    assertTrue(cc.isPresent());
271    // 1, A timeout split
272    // 1.1 close region
273    assertEquals(2, region.close(false).get(cf).size());
274    // 1.2 rollback and Region initialize again
275    region.initialize();
276
277    // 2, Run Compaction cc
278    assertFalse(region.compact(cc.get(), store, NoLimitThroughputController.INSTANCE));
279    assertTrue(fileNum > store.getStorefiles().size());
280
281    // 3, Split
282    requestSplitRegion(regionServer, region, Bytes.toBytes("row3"));
283    assertEquals(2, cluster.getRegions(tableName).size());
284  }
285
286  @Test
287  public void testSplitCompactWithPriority() throws Exception {
288    final TableName tableName = TableName.valueOf(name.getMethodName());
289    // Create table then get the single region for our new table.
290    byte[] cf = Bytes.toBytes("cf");
291    TableDescriptor htd = TableDescriptorBuilder.newBuilder(tableName)
292      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(cf)).build();
293    admin.createTable(htd);
294
295    assertNotEquals("Unable to retrieve regions of the table", -1,
296      TESTING_UTIL.waitFor(10000, () -> cluster.getRegions(tableName).size() == 1));
297
298    HRegion region = cluster.getRegions(tableName).get(0);
299    HStore store = region.getStore(cf);
300    int regionServerIndex = cluster.getServerWith(region.getRegionInfo().getRegionName());
301    HRegionServer regionServer = cluster.getRegionServer(regionServerIndex);
302
303    Table table = TESTING_UTIL.getConnection().getTable(tableName);
304    // insert data
305    insertData(tableName, admin, table);
306    insertData(tableName, admin, table, 20);
307    insertData(tableName, admin, table, 40);
308
309    // Compaction Request
310    store.triggerMajorCompaction();
311    Optional<CompactionContext> compactionContext = store.requestCompaction();
312    assertTrue(compactionContext.isPresent());
313    assertFalse(compactionContext.get().getRequest().isAfterSplit());
314    assertEquals(compactionContext.get().getRequest().getPriority(), 13);
315
316    // Split
317    long procId =
318      cluster.getMaster().splitRegion(region.getRegionInfo(), Bytes.toBytes("row4"), 0, 0);
319
320    // wait for the split to complete or get interrupted.  If the split completes successfully,
321    // the procedure will return true; if the split fails, the procedure would throw exception.
322    ProcedureTestingUtility.waitProcedure(cluster.getMaster().getMasterProcedureExecutor(),
323      procId);
324    Thread.sleep(3000);
325    assertNotEquals("Table is not split properly?", -1,
326      TESTING_UTIL.waitFor(3000,
327        () -> cluster.getRegions(tableName).size() == 2));
328    // we have 2 daughter regions
329    HRegion hRegion1 = cluster.getRegions(tableName).get(0);
330    HRegion hRegion2 = cluster.getRegions(tableName).get(1);
331    HStore hStore1 = hRegion1.getStore(cf);
332    HStore hStore2 = hRegion2.getStore(cf);
333
334    // For hStore1 && hStore2, set mock reference to one of the storeFiles
335    StoreFileInfo storeFileInfo1 = new ArrayList<>(hStore1.getStorefiles()).get(0).getFileInfo();
336    StoreFileInfo storeFileInfo2 = new ArrayList<>(hStore2.getStorefiles()).get(0).getFileInfo();
337    Field field = StoreFileInfo.class.getDeclaredField("reference");
338    field.setAccessible(true);
339    field.set(storeFileInfo1, Mockito.mock(Reference.class));
340    field.set(storeFileInfo2, Mockito.mock(Reference.class));
341    hStore1.triggerMajorCompaction();
342    hStore2.triggerMajorCompaction();
343
344    compactionContext = hStore1.requestCompaction();
345    assertTrue(compactionContext.isPresent());
346    // since we set mock reference to one of the storeFiles, we will get isAfterSplit=true &&
347    // highest priority for hStore1's compactionContext
348    assertTrue(compactionContext.get().getRequest().isAfterSplit());
349    assertEquals(compactionContext.get().getRequest().getPriority(), Integer.MIN_VALUE + 1000);
350
351    compactionContext =
352      hStore2.requestCompaction(Integer.MIN_VALUE + 10, CompactionLifeCycleTracker.DUMMY, null);
353    assertTrue(compactionContext.isPresent());
354    // compaction request contains higher priority than default priority of daughter region
355    // compaction (Integer.MIN_VALUE + 1000), hence we are expecting request priority to
356    // be accepted.
357    assertTrue(compactionContext.get().getRequest().isAfterSplit());
358    assertEquals(compactionContext.get().getRequest().getPriority(), Integer.MIN_VALUE + 10);
359    admin.disableTable(tableName);
360    admin.deleteTable(tableName);
361  }
362
363  public static class FailingSplitMasterObserver implements MasterCoprocessor, MasterObserver {
364    volatile CountDownLatch latch;
365
366    @Override
367    public void start(CoprocessorEnvironment e) throws IOException {
368      latch = new CountDownLatch(1);
369    }
370
371    @Override
372    public Optional<MasterObserver> getMasterObserver() {
373      return Optional.of(this);
374    }
375
376    @Override
377    public void preSplitRegionBeforeMETAAction(
378        final ObserverContext<MasterCoprocessorEnvironment> ctx,
379        final byte[] splitKey,
380        final List<Mutation> metaEntries) throws IOException {
381      latch.countDown();
382      throw new IOException("Causing rollback of region split");
383    }
384  }
385
386  @Test
387  public void testSplitRollbackOnRegionClosing() throws Exception {
388    final TableName tableName = TableName.valueOf(name.getMethodName());
389
390    // Create table then get the single region for our new table.
391    Table t = createTableAndWait(tableName, HConstants.CATALOG_FAMILY);
392    List<HRegion> regions = cluster.getRegions(tableName);
393    RegionInfo hri = getAndCheckSingleTableRegion(regions);
394
395    int tableRegionIndex = ensureTableRegionNotOnSameServerAsMeta(admin, hri);
396
397    RegionStates regionStates = cluster.getMaster().getAssignmentManager().getRegionStates();
398
399    // Turn off balancer so it doesn't cut in and mess up our placements.
400    this.admin.balancerSwitch(false, true);
401    // Turn off the meta scanner so it don't remove parent on us.
402    cluster.getMaster().setCatalogJanitorEnabled(false);
403    try {
404      // Add a bit of load up into the table so splittable.
405      TESTING_UTIL.loadTable(t, HConstants.CATALOG_FAMILY, false);
406      // Get region pre-split.
407      HRegionServer server = cluster.getRegionServer(tableRegionIndex);
408      printOutRegions(server, "Initial regions: ");
409      int regionCount = cluster.getRegions(hri.getTable()).size();
410      regionStates.updateRegionState(hri, RegionState.State.CLOSING);
411
412      // Now try splitting.... should fail.  And each should successfully
413      // rollback.
414      // We don't roll back here anymore. Instead we fail-fast on construction of the
415      // split transaction. Catch the exception instead.
416      try {
417        this.admin.splitRegionAsync(hri.getRegionName());
418        fail();
419      } catch (DoNotRetryRegionException e) {
420        // Expected
421      }
422      // Wait around a while and assert count of regions remains constant.
423      for (int i = 0; i < 10; i++) {
424        Thread.sleep(100);
425        assertEquals(regionCount, cluster.getRegions(hri.getTable()).size());
426      }
427      regionStates.updateRegionState(hri, State.OPEN);
428      // Now try splitting and it should work.
429      admin.splitRegionAsync(hri.getRegionName()).get(2, TimeUnit.MINUTES);
430      // Get daughters
431      checkAndGetDaughters(tableName);
432      // OK, so split happened after we cleared the blocking node.
433    } finally {
434      admin.balancerSwitch(true, false);
435      cluster.getMaster().setCatalogJanitorEnabled(true);
436      t.close();
437    }
438  }
439
440  /**
441   * Test that if daughter split on us, we won't do the shutdown handler fixup just because we can't
442   * find the immediate daughter of an offlined parent.
443   */
444  @Test
445  public void testShutdownFixupWhenDaughterHasSplit() throws Exception {
446    final TableName tableName = TableName.valueOf(name.getMethodName());
447
448    // Create table then get the single region for our new table.
449    Table t = createTableAndWait(tableName, HConstants.CATALOG_FAMILY); List<HRegion> regions =
450      cluster.getRegions(tableName); RegionInfo hri = getAndCheckSingleTableRegion(regions);
451    int tableRegionIndex = ensureTableRegionNotOnSameServerAsMeta(admin, hri);
452
453    // Turn off balancer so it doesn't cut in and mess up our placements.
454    this.admin.balancerSwitch(false, true);
455    // Turn off the meta scanner so it don't remove parent on us.
456    cluster.getMaster().setCatalogJanitorEnabled(false);
457    try {
458      // Add a bit of load up into the table so splittable.
459      TESTING_UTIL.loadTable(t, HConstants.CATALOG_FAMILY);
460      // Get region pre-split.
461      HRegionServer server = cluster.getRegionServer(tableRegionIndex);
462      printOutRegions(server, "Initial regions: ");
463      // Now split.
464      admin.splitRegionAsync(hri.getRegionName()).get(2, TimeUnit.MINUTES);
465      // Get daughters
466      List<HRegion> daughters = checkAndGetDaughters(tableName);
467      // Now split one of the daughters.
468      HRegion daughterRegion = daughters.get(0);
469      RegionInfo daughter = daughterRegion.getRegionInfo();
470      LOG.info("Daughter we are going to split: " + daughter);
471      clearReferences(daughterRegion);
472      LOG.info("Finished {} references={}", daughterRegion, daughterRegion.hasReferences());
473      admin.splitRegionAsync(daughter.getRegionName()).get(2, TimeUnit.MINUTES);
474      // Get list of daughters
475      daughters = cluster.getRegions(tableName);
476      for (HRegion d: daughters) {
477        LOG.info("Regions before crash: " + d);
478      }
479      // Now crash the server
480      cluster.abortRegionServer(tableRegionIndex);
481      waitUntilRegionServerDead();
482      awaitDaughters(tableName, daughters.size());
483      // Assert daughters are online and ONLY the original daughters -- that
484      // fixup didn't insert one during server shutdown recover.
485      regions = cluster.getRegions(tableName);
486      for (HRegion d: daughters) {
487        LOG.info("Regions after crash: " + d);
488      }
489      if (daughters.size() != regions.size()) {
490        LOG.info("Daughters=" + daughters.size() + ", regions=" + regions.size());
491      }
492      assertEquals(daughters.size(), regions.size());
493      for (HRegion r: regions) {
494        LOG.info("Regions post crash " + r + ", contains=" + daughters.contains(r));
495        assertTrue("Missing region post crash " + r, daughters.contains(r));
496      }
497    } finally {
498      LOG.info("EXITING");
499      admin.balancerSwitch(true, false);
500      cluster.getMaster().setCatalogJanitorEnabled(true);
501      t.close();
502    }
503  }
504
505  private void clearReferences(HRegion region) throws IOException {
506    // Presumption.
507    assertEquals(1, region.getStores().size());
508    HStore store = region.getStores().get(0);
509    while (store.hasReferences()) {
510      // Wait on any current compaction to complete first.
511      CompactionProgress progress = store.getCompactionProgress();
512      if (progress != null && progress.getProgressPct() < 1.0f) {
513        while (progress.getProgressPct() < 1.0f) {
514          LOG.info("Waiting, progress={}", progress.getProgressPct());
515          Threads.sleep(1000);
516        }
517      } else {
518        // Run new compaction. Shoudn't be any others running.
519        region.compact(true);
520      }
521      store.closeAndArchiveCompactedFiles();
522    }
523  }
524
525  @Test
526  public void testSplitShouldNotThrowNPEEvenARegionHasEmptySplitFiles() throws Exception {
527    TableName userTableName = TableName.valueOf(name.getMethodName());
528    TableDescriptor htd = TableDescriptorBuilder.newBuilder(userTableName)
529      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("col")).build();
530    admin.createTable(htd);
531    Table table = TESTING_UTIL.getConnection().getTable(userTableName);
532    try {
533      for (int i = 0; i <= 5; i++) {
534        String row = "row" + i;
535        Put p = new Put(row.getBytes());
536        String val = "Val" + i;
537        p.addColumn("col".getBytes(), "ql".getBytes(), val.getBytes());
538        table.put(p);
539        admin.flush(userTableName);
540        Delete d = new Delete(row.getBytes());
541        // Do a normal delete
542        table.delete(d);
543        admin.flush(userTableName);
544      }
545      admin.majorCompact(userTableName);
546      List<RegionInfo> regionsOfTable =
547          cluster.getMaster().getAssignmentManager().getRegionStates()
548          .getRegionsOfTable(userTableName);
549      assertEquals(1, regionsOfTable.size());
550      RegionInfo hRegionInfo = regionsOfTable.get(0);
551      Put p = new Put("row6".getBytes());
552      p.addColumn("col".getBytes(), "ql".getBytes(), "val".getBytes());
553      table.put(p);
554      p = new Put("row7".getBytes());
555      p.addColumn("col".getBytes(), "ql".getBytes(), "val".getBytes());
556      table.put(p);
557      p = new Put("row8".getBytes());
558      p.addColumn("col".getBytes(), "ql".getBytes(), "val".getBytes());
559      table.put(p);
560      admin.flush(userTableName);
561      admin.splitRegionAsync(hRegionInfo.getRegionName(), "row7".getBytes());
562      regionsOfTable = cluster.getMaster()
563          .getAssignmentManager().getRegionStates()
564          .getRegionsOfTable(userTableName);
565
566      while (regionsOfTable.size() != 2) {
567        Thread.sleep(1000);
568        regionsOfTable = cluster.getMaster()
569            .getAssignmentManager().getRegionStates()
570            .getRegionsOfTable(userTableName);
571        LOG.debug("waiting 2 regions to be available, got " + regionsOfTable.size() +
572          ": " + regionsOfTable);
573
574      }
575      Assert.assertEquals(2, regionsOfTable.size());
576
577      Scan s = new Scan();
578      ResultScanner scanner = table.getScanner(s);
579      int mainTableCount = 0;
580      for (Result rr = scanner.next(); rr != null; rr = scanner.next()) {
581        mainTableCount++;
582      }
583      Assert.assertEquals(3, mainTableCount);
584    } finally {
585      table.close();
586    }
587  }
588
589  /**
590   * Verifies HBASE-5806. Here the case is that splitting is completed but before the CJ could
591   * remove the parent region the master is killed and restarted.
592   */
593  @Test
594  public void testMasterRestartAtRegionSplitPendingCatalogJanitor()
595      throws IOException, InterruptedException, NodeExistsException, KeeperException,
596      ServiceException, ExecutionException, TimeoutException {
597    final TableName tableName = TableName.valueOf(name.getMethodName());
598    // Create table then get the single region for our new table.
599    try (Table t = createTableAndWait(tableName, HConstants.CATALOG_FAMILY)) {
600      List<HRegion> regions = cluster.getRegions(tableName);
601      RegionInfo hri = getAndCheckSingleTableRegion(regions);
602
603      int tableRegionIndex = ensureTableRegionNotOnSameServerAsMeta(admin, hri);
604
605      // Turn off balancer so it doesn't cut in and mess up our placements.
606      this.admin.balancerSwitch(false, true);
607      // Turn off the meta scanner so it don't remove parent on us.
608      cluster.getMaster().setCatalogJanitorEnabled(false);
609      // Add a bit of load up into the table so splittable.
610      TESTING_UTIL.loadTable(t, HConstants.CATALOG_FAMILY, false);
611      // Get region pre-split.
612      HRegionServer server = cluster.getRegionServer(tableRegionIndex);
613      printOutRegions(server, "Initial regions: ");
614      // Call split.
615      this.admin.splitRegionAsync(hri.getRegionName()).get(2, TimeUnit.MINUTES);
616      List<HRegion> daughters = checkAndGetDaughters(tableName);
617
618      // Before cleanup, get a new master.
619      HMaster master = abortAndWaitForMaster();
620      // Now call compact on the daughters and clean up any references.
621      for (HRegion daughter : daughters) {
622        clearReferences(daughter);
623        assertFalse(daughter.hasReferences());
624      }
625      // BUT calling compact on the daughters is not enough. The CatalogJanitor looks
626      // in the filesystem, and the filesystem content is not same as what the Region
627      // is reading from. Compacted-away files are picked up later by the compacted
628      // file discharger process. It runs infrequently. Make it run so CatalogJanitor
629      // doens't find any references.
630      for (RegionServerThread rst : cluster.getRegionServerThreads()) {
631        boolean oldSetting = rst.getRegionServer().compactedFileDischarger.setUseExecutor(false);
632        rst.getRegionServer().compactedFileDischarger.run();
633        rst.getRegionServer().compactedFileDischarger.setUseExecutor(oldSetting);
634      }
635      cluster.getMaster().setCatalogJanitorEnabled(true);
636      ProcedureTestingUtility.waitAllProcedures(cluster.getMaster().getMasterProcedureExecutor());
637      LOG.info("Starting run of CatalogJanitor");
638      cluster.getMaster().getCatalogJanitor().run();
639      ProcedureTestingUtility.waitAllProcedures(cluster.getMaster().getMasterProcedureExecutor());
640      RegionStates regionStates = master.getAssignmentManager().getRegionStates();
641      ServerName regionServerOfRegion = regionStates.getRegionServerOfRegion(hri);
642      assertEquals(null, regionServerOfRegion);
643    } finally {
644      TESTING_UTIL.getAdmin().balancerSwitch(true, false);
645      cluster.getMaster().setCatalogJanitorEnabled(true);
646    }
647  }
648
649  @Test
650  public void testSplitWithRegionReplicas() throws Exception {
651    final TableName tableName = TableName.valueOf(name.getMethodName());
652    HTableDescriptor htd = TESTING_UTIL.createTableDescriptor(name.getMethodName());
653    htd.setRegionReplication(2);
654    htd.addCoprocessor(SlowMeCopro.class.getName());
655    // Create table then get the single region for our new table.
656    Table t = TESTING_UTIL.createTable(htd, new byte[][]{Bytes.toBytes("cf")}, null);
657    List<HRegion> oldRegions;
658    do {
659      oldRegions = cluster.getRegions(tableName);
660      Thread.sleep(10);
661    } while (oldRegions.size() != 2);
662    for (HRegion h : oldRegions) LOG.debug("OLDREGION " + h.getRegionInfo());
663    try {
664      int regionServerIndex = cluster.getServerWith(oldRegions.get(0).getRegionInfo()
665        .getRegionName());
666      HRegionServer regionServer = cluster.getRegionServer(regionServerIndex);
667      insertData(tableName, admin, t);
668      // Turn off balancer so it doesn't cut in and mess up our placements.
669      admin.balancerSwitch(false, true);
670      // Turn off the meta scanner so it don't remove parent on us.
671      cluster.getMaster().setCatalogJanitorEnabled(false);
672      boolean tableExists = TESTING_UTIL.getAdmin().tableExists(tableName);
673      assertEquals("The specified table should be present.", true, tableExists);
674      final HRegion region = findSplittableRegion(oldRegions);
675      regionServerIndex = cluster.getServerWith(region.getRegionInfo().getRegionName());
676      regionServer = cluster.getRegionServer(regionServerIndex);
677      assertTrue("not able to find a splittable region", region != null);
678      try {
679        requestSplitRegion(regionServer, region, Bytes.toBytes("row2"));
680      } catch (IOException e) {
681        e.printStackTrace();
682        fail("Split execution should have succeeded with no exceptions thrown " + e);
683      }
684      //TESTING_UTIL.waitUntilAllRegionsAssigned(tableName);
685      List<HRegion> newRegions;
686      do {
687        newRegions = cluster.getRegions(tableName);
688        for (HRegion h : newRegions) LOG.debug("NEWREGION " + h.getRegionInfo());
689        Thread.sleep(1000);
690      } while ((newRegions.contains(oldRegions.get(0)) || newRegions.contains(oldRegions.get(1)))
691          || newRegions.size() != 4);
692      tableExists = TESTING_UTIL.getAdmin().tableExists(tableName);
693      assertEquals("The specified table should be present.", true, tableExists);
694      // exists works on stale and we see the put after the flush
695      byte[] b1 = "row1".getBytes();
696      Get g = new Get(b1);
697      g.setConsistency(Consistency.STRONG);
698      // The following GET will make a trip to the meta to get the new location of the 1st daughter
699      // In the process it will also get the location of the replica of the daughter (initially
700      // pointing to the parent's replica)
701      Result r = t.get(g);
702      Assert.assertFalse(r.isStale());
703      LOG.info("exists stale after flush done");
704
705      SlowMeCopro.getPrimaryCdl().set(new CountDownLatch(1));
706      g = new Get(b1);
707      g.setConsistency(Consistency.TIMELINE);
708      // This will succeed because in the previous GET we get the location of the replica
709      r = t.get(g);
710      Assert.assertTrue(r.isStale());
711      SlowMeCopro.getPrimaryCdl().get().countDown();
712    } finally {
713      SlowMeCopro.getPrimaryCdl().get().countDown();
714      admin.balancerSwitch(true, false);
715      cluster.getMaster().setCatalogJanitorEnabled(true);
716      t.close();
717    }
718  }
719
720  private void insertData(final TableName tableName, Admin admin, Table t) throws IOException {
721    insertData(tableName, admin, t, 1);
722  }
723
724  private void insertData(TableName tableName, Admin admin, Table t, int i) throws IOException {
725    Put p = new Put(Bytes.toBytes("row" + i));
726    p.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("q1"), Bytes.toBytes("1"));
727    t.put(p);
728    p = new Put(Bytes.toBytes("row" + (i + 1)));
729    p.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("q1"), Bytes.toBytes("2"));
730    t.put(p);
731    p = new Put(Bytes.toBytes("row" + (i + 2)));
732    p.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("q1"), Bytes.toBytes("3"));
733    t.put(p);
734    p = new Put(Bytes.toBytes("row" + (i + 3)));
735    p.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("q1"), Bytes.toBytes("4"));
736    t.put(p);
737    admin.flush(tableName);
738  }
739
740  /**
741   * If a table has regions that have no store files in a region, they should split successfully
742   * into two regions with no store files.
743   */
744  @Test
745  public void testSplitRegionWithNoStoreFiles() throws Exception {
746    final TableName tableName = TableName.valueOf(name.getMethodName());
747    // Create table then get the single region for our new table.
748    createTableAndWait(tableName, HConstants.CATALOG_FAMILY);
749    List<HRegion> regions = cluster.getRegions(tableName);
750    RegionInfo hri = getAndCheckSingleTableRegion(regions);
751    ensureTableRegionNotOnSameServerAsMeta(admin, hri);
752    int regionServerIndex = cluster.getServerWith(regions.get(0).getRegionInfo()
753      .getRegionName());
754    HRegionServer regionServer = cluster.getRegionServer(regionServerIndex);
755    // Turn off balancer so it doesn't cut in and mess up our placements.
756    this.admin.balancerSwitch(false, true);
757    // Turn off the meta scanner so it don't remove parent on us.
758    cluster.getMaster().setCatalogJanitorEnabled(false);
759    try {
760      // Precondition: we created a table with no data, no store files.
761      printOutRegions(regionServer, "Initial regions: ");
762      Configuration conf = cluster.getConfiguration();
763      HBaseFsck.debugLsr(conf, new Path("/"));
764      Path rootDir = CommonFSUtils.getRootDir(conf);
765      FileSystem fs = TESTING_UTIL.getDFSCluster().getFileSystem();
766      Map<String, Path> storefiles =
767          FSUtils.getTableStoreFilePathMap(null, fs, rootDir, tableName);
768      assertEquals("Expected nothing but found " + storefiles.toString(), 0, storefiles.size());
769
770      // find a splittable region.  Refresh the regions list
771      regions = cluster.getRegions(tableName);
772      final HRegion region = findSplittableRegion(regions);
773      assertTrue("not able to find a splittable region", region != null);
774
775      // Now split.
776      try {
777        requestSplitRegion(regionServer, region, Bytes.toBytes("row2"));
778      } catch (IOException e) {
779        fail("Split execution should have succeeded with no exceptions thrown");
780      }
781
782      // Postcondition: split the table with no store files into two regions, but still have no
783      // store files
784      List<HRegion> daughters = cluster.getRegions(tableName);
785      assertEquals(2, daughters.size());
786
787      // check dirs
788      HBaseFsck.debugLsr(conf, new Path("/"));
789      Map<String, Path> storefilesAfter =
790          FSUtils.getTableStoreFilePathMap(null, fs, rootDir, tableName);
791      assertEquals("Expected nothing but found " + storefilesAfter.toString(), 0,
792          storefilesAfter.size());
793
794      hri = region.getRegionInfo(); // split parent
795      AssignmentManager am = cluster.getMaster().getAssignmentManager();
796      RegionStates regionStates = am.getRegionStates();
797      long start = EnvironmentEdgeManager.currentTime();
798      while (!regionStates.isRegionInState(hri, State.SPLIT)) {
799        LOG.debug("Waiting for SPLIT state on: " + hri);
800        assertFalse("Timed out in waiting split parent to be in state SPLIT",
801          EnvironmentEdgeManager.currentTime() - start > 60000);
802        Thread.sleep(500);
803      }
804      assertTrue(regionStates.isRegionInState(daughters.get(0).getRegionInfo(), State.OPEN));
805      assertTrue(regionStates.isRegionInState(daughters.get(1).getRegionInfo(), State.OPEN));
806
807      // We should not be able to assign it again
808      try {
809        am.assign(hri);
810      } catch (DoNotRetryIOException e) {
811        // Expected
812      }
813      assertFalse("Split region can't be assigned", regionStates.isRegionInTransition(hri));
814      assertTrue(regionStates.isRegionInState(hri, State.SPLIT));
815
816      // We should not be able to unassign it either
817      try {
818        am.unassign(hri);
819        fail("Should have thrown exception");
820      } catch (DoNotRetryIOException e) {
821        // Expected
822      }
823      assertFalse("Split region can't be unassigned", regionStates.isRegionInTransition(hri));
824      assertTrue(regionStates.isRegionInState(hri, State.SPLIT));
825    } finally {
826      admin.balancerSwitch(true, false);
827      cluster.getMaster().setCatalogJanitorEnabled(true);
828    }
829  }
830
831  @Test
832  public void testStoreFileReferenceCreationWhenSplitPolicySaysToSkipRangeCheck()
833      throws Exception {
834    final TableName tableName = TableName.valueOf(name.getMethodName());
835    try {
836      byte[] cf = Bytes.toBytes("f");
837      byte[] cf1 = Bytes.toBytes("i_f");
838      TableDescriptor htd = TableDescriptorBuilder.newBuilder(tableName)
839        .setColumnFamily(ColumnFamilyDescriptorBuilder.of(cf))
840        .setColumnFamily(ColumnFamilyDescriptorBuilder.of(cf1))
841        .setRegionSplitPolicyClassName(CustomSplitPolicy.class.getName()).build();
842      admin.createTable(htd);
843      List<HRegion> regions = awaitTableRegions(tableName);
844      HRegion region = regions.get(0);
845      for(int i = 3;i<9;i++) {
846        Put p = new Put(Bytes.toBytes("row"+i));
847        p.addColumn(cf, Bytes.toBytes("q"), Bytes.toBytes("value" + i));
848        p.addColumn(cf1, Bytes.toBytes("q"), Bytes.toBytes("value" + i));
849        region.put(p);
850      }
851      region.flush(true);
852      HStore store = region.getStore(cf);
853      Collection<HStoreFile> storefiles = store.getStorefiles();
854      assertEquals(1, storefiles.size());
855      assertFalse(region.hasReferences());
856      Path referencePath =
857          region.getRegionFileSystem().splitStoreFile(region.getRegionInfo(), "f",
858            storefiles.iterator().next(), Bytes.toBytes("row1"), false, region.getSplitPolicy());
859      assertNull(referencePath);
860      referencePath =
861          region.getRegionFileSystem().splitStoreFile(region.getRegionInfo(), "i_f",
862            storefiles.iterator().next(), Bytes.toBytes("row1"), false, region.getSplitPolicy());
863      assertNotNull(referencePath);
864    } finally {
865      TESTING_UTIL.deleteTable(tableName);
866    }
867  }
868
869  private HRegion findSplittableRegion(final List<HRegion> regions) throws InterruptedException {
870    for (int i = 0; i < 5; ++i) {
871      for (HRegion r: regions) {
872        if (r.isSplittable() && r.getRegionInfo().getReplicaId() == 0) {
873          return(r);
874        }
875      }
876      Thread.sleep(100);
877    }
878    return null;
879  }
880
881  private List<HRegion> checkAndGetDaughters(TableName tableName) throws InterruptedException {
882    List<HRegion> daughters = null;
883    // try up to 10s
884    for (int i = 0; i < 100; i++) {
885      daughters = cluster.getRegions(tableName);
886      if (daughters.size() >= 2) {
887        break;
888      }
889      Thread.sleep(100);
890    }
891    assertTrue(daughters.size() >= 2);
892    return daughters;
893  }
894
895  private HMaster abortAndWaitForMaster() throws IOException, InterruptedException {
896    cluster.abortMaster(0);
897    cluster.waitOnMaster(0);
898    HMaster master = cluster.startMaster().getMaster();
899    cluster.waitForActiveAndReadyMaster();
900    // reset the connections
901    IOUtils.closeQuietly(admin);
902    TESTING_UTIL.invalidateConnection();
903    admin = TESTING_UTIL.getAdmin();
904    return master;
905  }
906
907  /**
908   * Ensure single table region is not on same server as the single hbase:meta table
909   * region.
910   * @return Index of the server hosting the single table region
911   * @throws UnknownRegionException
912   * @throws MasterNotRunningException
913   * @throws org.apache.hadoop.hbase.ZooKeeperConnectionException
914   * @throws InterruptedException
915   */
916  private int ensureTableRegionNotOnSameServerAsMeta(final Admin admin,
917      final RegionInfo hri)
918  throws IOException, MasterNotRunningException,
919  ZooKeeperConnectionException, InterruptedException {
920    // Now make sure that the table region is not on same server as that hosting
921    // hbase:meta  We don't want hbase:meta replay polluting our test when we later crash
922    // the table region serving server.
923    int metaServerIndex = cluster.getServerWithMeta();
924    boolean tablesOnMaster = LoadBalancer.isTablesOnMaster(TESTING_UTIL.getConfiguration());
925    if (tablesOnMaster) {
926      // Need to check master is supposed to host meta... perhaps it is not.
927      throw new UnsupportedOperationException();
928      // TODO: assertTrue(metaServerIndex == -1); // meta is on master now
929    }
930    HRegionServer metaRegionServer = tablesOnMaster?
931      cluster.getMaster(): cluster.getRegionServer(metaServerIndex);
932    int tableRegionIndex = cluster.getServerWith(hri.getRegionName());
933    assertTrue(tableRegionIndex != -1);
934    HRegionServer tableRegionServer = cluster.getRegionServer(tableRegionIndex);
935    LOG.info("MetaRegionServer=" + metaRegionServer.getServerName() +
936      ", other=" + tableRegionServer.getServerName());
937    if (metaRegionServer.getServerName().equals(tableRegionServer.getServerName())) {
938      HRegionServer hrs = getOtherRegionServer(cluster, metaRegionServer);
939      assertNotNull(hrs);
940      assertNotNull(hri);
941      LOG.info("Moving " + hri.getRegionNameAsString() + " from " +
942        metaRegionServer.getServerName() + " to " +
943        hrs.getServerName() + "; metaServerIndex=" + metaServerIndex);
944      admin.move(hri.getEncodedNameAsBytes(), hrs.getServerName());
945    }
946    // Wait till table region is up on the server that is NOT carrying hbase:meta.
947    for (int i = 0; i < 100; i++) {
948      tableRegionIndex = cluster.getServerWith(hri.getRegionName());
949      if (tableRegionIndex != -1 && tableRegionIndex != metaServerIndex) break;
950      LOG.debug("Waiting on region move off the hbase:meta server; current index " +
951        tableRegionIndex + " and metaServerIndex=" + metaServerIndex);
952      Thread.sleep(100);
953    }
954    assertTrue("Region not moved off hbase:meta server, tableRegionIndex=" + tableRegionIndex,
955      tableRegionIndex != -1 && tableRegionIndex != metaServerIndex);
956    // Verify for sure table region is not on same server as hbase:meta
957    tableRegionIndex = cluster.getServerWith(hri.getRegionName());
958    assertTrue(tableRegionIndex != -1);
959    assertNotSame(metaServerIndex, tableRegionIndex);
960    return tableRegionIndex;
961  }
962
963  /**
964   * Find regionserver other than the one passed.
965   * Can't rely on indexes into list of regionservers since crashed servers
966   * occupy an index.
967   * @param cluster
968   * @param notThisOne
969   * @return A regionserver that is not <code>notThisOne</code> or null if none
970   * found
971   */
972  private HRegionServer getOtherRegionServer(final MiniHBaseCluster cluster,
973      final HRegionServer notThisOne) {
974    for (RegionServerThread rst: cluster.getRegionServerThreads()) {
975      HRegionServer hrs = rst.getRegionServer();
976      if (hrs.getServerName().equals(notThisOne.getServerName())) continue;
977      if (hrs.isStopping() || hrs.isStopped()) continue;
978      return hrs;
979    }
980    return null;
981  }
982
983  private void printOutRegions(final HRegionServer hrs, final String prefix)
984      throws IOException {
985    List<RegionInfo> regions = ProtobufUtil.getOnlineRegions(hrs.getRSRpcServices());
986    for (RegionInfo region: regions) {
987      LOG.info(prefix + region.getRegionNameAsString());
988    }
989  }
990
991  private void waitUntilRegionServerDead() throws InterruptedException, IOException {
992    // Wait until the master processes the RS shutdown
993    for (int i=0; (cluster.getMaster().getClusterMetrics()
994        .getLiveServerMetrics().size() > NB_SERVERS
995        || cluster.getLiveRegionServerThreads().size() > NB_SERVERS) && i<100; i++) {
996      LOG.info("Waiting on server to go down");
997      Thread.sleep(100);
998    }
999    assertFalse("Waited too long for RS to die",
1000      cluster.getMaster().getClusterMetrics(). getLiveServerMetrics().size() > NB_SERVERS
1001        || cluster.getLiveRegionServerThreads().size() > NB_SERVERS);
1002  }
1003
1004  private void awaitDaughters(TableName tableName, int numDaughters) throws InterruptedException {
1005    // Wait till regions are back on line again.
1006    for (int i = 0; cluster.getRegions(tableName).size() < numDaughters && i < 60; i++) {
1007      LOG.info("Waiting for repair to happen");
1008      Thread.sleep(1000);
1009    }
1010    if (cluster.getRegions(tableName).size() < numDaughters) {
1011      fail("Waiting too long for daughter regions");
1012    }
1013  }
1014
1015  private List<HRegion> awaitTableRegions(final TableName tableName) throws InterruptedException {
1016    List<HRegion> regions = null;
1017    for (int i = 0; i < 100; i++) {
1018      regions = cluster.getRegions(tableName);
1019      if (regions.size() > 0) break;
1020      Thread.sleep(100);
1021    }
1022    return regions;
1023  }
1024
1025  private Table createTableAndWait(TableName tableName, byte[] cf) throws IOException,
1026      InterruptedException {
1027    Table t = TESTING_UTIL.createTable(tableName, cf);
1028    awaitTableRegions(tableName);
1029    assertTrue("Table not online: " + tableName,
1030      cluster.getRegions(tableName).size() != 0);
1031    return t;
1032  }
1033
1034  // Make it public so that JVMClusterUtil can access it.
1035  public static class MyMaster extends HMaster {
1036    public MyMaster(Configuration conf) throws IOException, KeeperException, InterruptedException {
1037      super(conf);
1038    }
1039
1040    @Override
1041    protected RSRpcServices createRpcServices() throws IOException {
1042      return new MyMasterRpcServices(this);
1043    }
1044  }
1045
1046  static class MyMasterRpcServices extends MasterRpcServices {
1047    static AtomicBoolean enabled = new AtomicBoolean(false);
1048
1049    private HMaster myMaster;
1050    public MyMasterRpcServices(HMaster master) throws IOException {
1051      super(master);
1052      myMaster = master;
1053    }
1054
1055    @Override
1056    public ReportRegionStateTransitionResponse reportRegionStateTransition(RpcController c,
1057        ReportRegionStateTransitionRequest req) throws ServiceException {
1058      ReportRegionStateTransitionResponse resp = super.reportRegionStateTransition(c, req);
1059      if (enabled.get() && req.getTransition(0).getTransitionCode().equals(
1060          TransitionCode.READY_TO_SPLIT) && !resp.hasErrorMessage()) {
1061        RegionStates regionStates = myMaster.getAssignmentManager().getRegionStates();
1062        for (RegionStateNode regionState:
1063          regionStates.getRegionsInTransition()) {
1064          /* TODO!!!!
1065          // Find the merging_new region and remove it
1066          if (regionState.isSplittingNew()) {
1067            regionStates.deleteRegion(regionState.getRegion());
1068          }
1069          */
1070        }
1071      }
1072      return resp;
1073    }
1074  }
1075
1076  static class CustomSplitPolicy extends IncreasingToUpperBoundRegionSplitPolicy {
1077
1078    @Override
1079    protected boolean shouldSplit() {
1080      return true;
1081    }
1082
1083    @Override
1084    public boolean skipStoreFileRangeCheck(String familyName) {
1085      if(familyName.startsWith("i_")) {
1086        return true;
1087      } else {
1088        return false;
1089      }
1090    }
1091  }
1092}
1093