View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.regionserver;
19  
20  import static org.junit.Assert.assertArrayEquals;
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertFalse;
23  import static org.junit.Assert.assertTrue;
24  
25  import java.io.IOException;
26  import java.util.ArrayList;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.NavigableMap;
30  import java.util.Random;
31  import java.util.Set;
32  import java.util.TreeSet;
33  import java.util.concurrent.TimeUnit;
34  
35  import org.apache.commons.io.IOUtils;
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  import org.apache.hadoop.conf.Configuration;
39  import org.apache.hadoop.hbase.ChoreService;
40  import org.apache.hadoop.hbase.HBaseTestingUtility;
41  import org.apache.hadoop.hbase.HColumnDescriptor;
42  import org.apache.hadoop.hbase.HConstants;
43  import org.apache.hadoop.hbase.HRegionInfo;
44  import org.apache.hadoop.hbase.HRegionLocation;
45  import org.apache.hadoop.hbase.HTableDescriptor;
46  import org.apache.hadoop.hbase.MetaTableAccessor;
47  import org.apache.hadoop.hbase.NotServingRegionException;
48  import org.apache.hadoop.hbase.ScheduledChore;
49  import org.apache.hadoop.hbase.ServerName;
50  import org.apache.hadoop.hbase.Stoppable;
51  import org.apache.hadoop.hbase.TableName;
52  import org.apache.hadoop.hbase.client.Admin;
53  import org.apache.hadoop.hbase.client.Connection;
54  import org.apache.hadoop.hbase.client.ConnectionFactory;
55  import org.apache.hadoop.hbase.client.Get;
56  import org.apache.hadoop.hbase.client.HTable;
57  import org.apache.hadoop.hbase.client.MetaScanner;
58  import org.apache.hadoop.hbase.client.Put;
59  import org.apache.hadoop.hbase.client.Result;
60  import org.apache.hadoop.hbase.client.Scan;
61  import org.apache.hadoop.hbase.client.Table;
62  import org.apache.hadoop.hbase.coordination.BaseCoordinatedStateManager;
63  import org.apache.hadoop.hbase.ipc.HBaseRpcControllerImpl;
64  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
65  import org.apache.hadoop.hbase.protobuf.RequestConverter;
66  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetRegionInfoResponse.CompactionState;
67  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
68  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ScanRequest;
69  import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos;
70  import org.apache.hadoop.hbase.testclassification.LargeTests;
71  import org.apache.hadoop.hbase.util.Bytes;
72  import org.apache.hadoop.hbase.util.Pair;
73  import org.apache.hadoop.hbase.util.PairOfSameType;
74  import org.apache.hadoop.hbase.util.RetryCounter;
75  import org.apache.hadoop.hbase.util.StoppableImplementation;
76  import org.apache.hadoop.hbase.util.Threads;
77  import org.junit.AfterClass;
78  import org.junit.Assert;
79  import org.junit.BeforeClass;
80  import org.junit.Test;
81  import org.junit.experimental.categories.Category;
82  
83  import com.google.common.collect.Iterators;
84  import com.google.common.collect.Maps;
85  import com.google.common.collect.Sets;
86  import com.google.protobuf.ServiceException;
87  
88  @Category(LargeTests.class)
89  public class TestEndToEndSplitTransaction {
90    private static final Log LOG = LogFactory.getLog(TestEndToEndSplitTransaction.class);
91    private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
92    private static final Configuration CONF = TEST_UTIL.getConfiguration();
93  
94    @BeforeClass
95    public static void beforeAllTests() throws Exception {
96      TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 5);
97      TEST_UTIL.startMiniCluster();
98    }
99  
100   @AfterClass
101   public static void afterAllTests() throws Exception {
102     TEST_UTIL.shutdownMiniCluster();
103   }
104 
105   /*
106    * This is the test for : HBASE-20940 This test will split the region and try to open an reference
107    * over store file. Once store file has any reference, it makes sure that region can't be split
108    * @throws Exception
109    */
110   @Test
111   public void testCanSplitJustAfterASplit() throws Exception {
112     LOG.info("Starting testCanSplitJustAfterASplit");
113     byte[] fam = Bytes.toBytes("cf_split");
114 
115     TableName tableName = TableName.valueOf("CanSplitTable");
116     Table source = TEST_UTIL.getConnection().getTable(tableName);
117     Admin admin = TEST_UTIL.getHBaseAdmin();
118     Map<String, StoreFile.Reader> scanner = Maps.newHashMap();
119 
120     try {
121       HTableDescriptor htd = new HTableDescriptor(tableName);
122       htd.addFamily(new HColumnDescriptor(fam));
123 
124       admin.createTable(htd);
125       TEST_UTIL.loadTable(source, fam);
126       List<HRegion> regions = TEST_UTIL.getHBaseCluster().getRegions(tableName);
127       regions.get(0).forceSplit(null);
128       admin.split(tableName);
129 
130       while (regions.size() <= 1) {
131         regions = TEST_UTIL.getHBaseCluster().getRegions(tableName);
132         // Trying to get reference of the file before region split completes
133         // this would try to get handle on storefile or it can not be cleaned
134         for (HRegion region : regions) {
135           for (Store store : region.getStores()) {
136             for (StoreFile file : store.getStorefiles()) {
137               StoreFile.Reader reader = file.getReader();
138               reader.getStoreFileScanner(false, false, false, 0, 0, false);
139               scanner.put(region.getRegionInfo().getEncodedName(), reader);
140               LOG.info("Got reference to file = " + file.getPath() + ",for region = "
141                   + region.getRegionInfo().getEncodedName());
142             }
143           }
144         }
145       }
146 
147       Assert.assertTrue("Regions did not split properly", regions.size() > 1);
148       Assert.assertTrue("Could not get reference any of the store file", scanner.size() > 1);
149 
150       RetryCounter retrier = new RetryCounter(30, 1, TimeUnit.SECONDS);
151       while (CompactionState.NONE != admin.getCompactionState(tableName) && retrier.shouldRetry()) {
152         retrier.sleepUntilNextRetry();
153       }
154 
155       Assert.assertEquals("Compaction did not complete in 30 secs", CompactionState.NONE,
156         admin.getCompactionState(tableName));
157 
158       for (HRegion region : regions) {
159         for (Store store : region.getStores()) {
160           Assert.assertTrue("Contains an open file reference which can be split",
161             !scanner.containsKey(region.getRegionInfo().getEncodedName()) || !store.canSplit());
162         }
163       }
164     } finally {
165       for (StoreFile.Reader s : scanner.values()) {
166         try {
167           s.close(true);
168         } catch (IOException e) {
169           e.printStackTrace();
170         }
171       }
172       scanner.clear();
173       if (source != null) source.close();
174       TEST_UTIL.deleteTableIfAny(tableName);
175     }
176   }
177 
178   @Test
179   public void testMasterOpsWhileSplitting() throws Exception {
180     TableName tableName = TableName.valueOf("TestSplit");
181     byte[] familyName = Bytes.toBytes("fam");
182     try (HTable ht = TEST_UTIL.createTable(tableName, familyName)) {
183       TEST_UTIL.loadTable(ht, familyName, false);
184     }
185     HRegionServer server = TEST_UTIL.getHBaseCluster().getRegionServer(0);
186     byte[] firstRow = Bytes.toBytes("aaa");
187     byte[] splitRow = Bytes.toBytes("lll");
188     byte[] lastRow = Bytes.toBytes("zzz");
189     try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) {
190       // this will also cache the region
191       byte[] regionName = conn.getRegionLocator(tableName).getRegionLocation(splitRow)
192           .getRegionInfo().getRegionName();
193       Region region = server.getRegion(regionName);
194       SplitTransactionImpl split = new SplitTransactionImpl((HRegion) region, splitRow);
195       split.prepare();
196 
197       // 1. phase I
198       PairOfSameType<Region> regions = split.createDaughters(server, server, null);
199       assertFalse(test(conn, tableName, firstRow, server));
200       assertFalse(test(conn, tableName, lastRow, server));
201 
202       // passing null as services prevents final step
203       // 2, most of phase II
204       split.openDaughters(server, null, regions.getFirst(), regions.getSecond());
205       assertFalse(test(conn, tableName, firstRow, server));
206       assertFalse(test(conn, tableName, lastRow, server));
207 
208       // 3. finish phase II
209       // note that this replicates some code from SplitTransaction
210       // 2nd daughter first
211       if (split.useZKForAssignment) {
212         server.postOpenDeployTasks(regions.getSecond());
213       } else {
214         server.reportRegionStateTransition(
215           RegionServerStatusProtos.RegionStateTransition.TransitionCode.SPLIT,
216           region.getRegionInfo(), regions.getFirst().getRegionInfo(),
217           regions.getSecond().getRegionInfo());
218       }
219 
220       // first daughter second
221       if (split.useZKForAssignment) {
222         server.postOpenDeployTasks(regions.getFirst());
223       }
224 
225       // Add to online regions
226       server.addToOnlineRegions(regions.getSecond());
227       // THIS is the crucial point:
228       // the 2nd daughter was added, so querying before the split key should fail.
229       assertFalse(test(conn, tableName, firstRow, server));
230       // past splitkey is ok.
231       assertTrue(test(conn, tableName, lastRow, server));
232 
233       // Add to online regions
234       server.addToOnlineRegions(regions.getFirst());
235       assertTrue(test(conn, tableName, firstRow, server));
236       assertTrue(test(conn, tableName, lastRow, server));
237 
238       if (split.useZKForAssignment) {
239         // 4. phase III
240         ((BaseCoordinatedStateManager) server.getCoordinatedStateManager())
241             .getSplitTransactionCoordination().completeSplitTransaction(server, regions.getFirst(),
242               regions.getSecond(), split.std, region);
243       }
244 
245       assertTrue(test(conn, tableName, firstRow, server));
246       assertTrue(test(conn, tableName, lastRow, server));
247     }
248   }
249 
250   @Test
251   public void testTableAvailableWhileSplitting() throws Exception {
252     TableName tableName = TableName.valueOf("TestTableAvailableWhileSplitting");
253     byte[] familyName = Bytes.toBytes("fam");
254     try (HTable ht = TEST_UTIL.createTable(tableName, familyName)) {
255       TEST_UTIL.loadTable(ht, familyName, false);
256     }
257     Admin admin = TEST_UTIL.getHBaseAdmin();
258     HRegionServer server = TEST_UTIL.getHBaseCluster().getRegionServer(0);
259     byte[] splitRow = Bytes.toBytes("lll");
260     try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) {
261       byte[] regionName = conn.getRegionLocator(tableName).getRegionLocation(splitRow)
262           .getRegionInfo().getRegionName();
263       Region region = server.getRegion(regionName);
264       SplitTransactionImpl split = new SplitTransactionImpl((HRegion) region, splitRow);
265       split.prepare();
266       assertTrue(admin.isTableAvailable(tableName));
267 
268       // 1. phase I
269       PairOfSameType<Region> regions = split.createDaughters(server, server, null);
270       // Parent should be offline at this stage and daughters not yet open
271       assertFalse(admin.isTableAvailable(tableName));
272 
273       // passing null as services prevents final step of postOpenDeployTasks
274       // 2, most of phase II
275       split.openDaughters(server, null, regions.getFirst(), regions.getSecond());
276       assertFalse(admin.isTableAvailable(tableName));
277 
278       // Finish openeing daughters
279       // 2nd daughter first
280       if (split.useZKForAssignment) {
281         server.postOpenDeployTasks(regions.getSecond());
282       } else {
283         server.reportRegionStateTransition(
284           RegionServerStatusProtos.RegionStateTransition.TransitionCode.SPLIT,
285           region.getRegionInfo(), regions.getFirst().getRegionInfo(),
286           regions.getSecond().getRegionInfo());
287       }
288 
289       // first daughter second
290       if (split.useZKForAssignment) {
291         server.postOpenDeployTasks(regions.getFirst());
292       }
293 
294       // After postOpenDeploy daughters should have location in meta
295       assertTrue(admin.isTableAvailable(tableName));
296 
297       server.addToOnlineRegions(regions.getSecond());
298       server.addToOnlineRegions(regions.getFirst());
299       assertTrue(admin.isTableAvailable(tableName));
300     } finally {
301       if (admin != null) {
302         admin.close();
303       }
304     }
305   }
306 
307   /**
308    * attempt to locate the region and perform a get and scan
309    * @return True if successful, False otherwise.
310    */
311   private boolean test(Connection conn, TableName tableName, byte[] row,
312       HRegionServer server) {
313     // not using HTable to avoid timeouts and retries
314     try {
315       byte[] regionName = conn.getRegionLocator(tableName).getRegionLocation(row, true)
316           .getRegionInfo().getRegionName();
317       // get and scan should now succeed without exception
318       ClientProtos.GetRequest request =
319           RequestConverter.buildGetRequest(regionName, new Get(row));
320       server.getRSRpcServices().get(null, request);
321       ScanRequest scanRequest = RequestConverter.buildScanRequest(
322         regionName, new Scan(row), 1, true);
323       try {
324         server.getRSRpcServices().scan(
325           new HBaseRpcControllerImpl(), scanRequest);
326       } catch (ServiceException se) {
327         throw ProtobufUtil.getRemoteException(se);
328       }
329     } catch (IOException e) {
330       return false;
331     } catch (ServiceException e) {
332       return false;
333     }
334     return true;
335   }
336 
337   /**
338    * Tests that the client sees meta table changes as atomic during splits
339    */
340   @Test
341   public void testFromClientSideWhileSplitting() throws Throwable {
342     LOG.info("Starting testFromClientSideWhileSplitting");
343     final TableName TABLENAME =
344         TableName.valueOf("testFromClientSideWhileSplitting");
345     final byte[] FAMILY = Bytes.toBytes("family");
346 
347     //SplitTransaction will update the meta table by offlining the parent region, and adding info
348     //for daughters.
349     Table table = TEST_UTIL.createTable(TABLENAME, FAMILY);
350 
351     Stoppable stopper = new StoppableImplementation();
352     RegionSplitter regionSplitter = new RegionSplitter(table);
353     RegionChecker regionChecker = new RegionChecker(CONF, stopper, TABLENAME);
354     final ChoreService choreService = new ChoreService("TEST_SERVER");
355 
356     choreService.scheduleChore(regionChecker);
357     regionSplitter.start();
358 
359     //wait until the splitter is finished
360     regionSplitter.join();
361     stopper.stop(null);
362 
363     if (regionChecker.ex != null) {
364       throw regionChecker.ex;
365     }
366 
367     if (regionSplitter.ex != null) {
368       throw regionSplitter.ex;
369     }
370 
371     //one final check
372     regionChecker.verify();
373   }
374 
375   static class RegionSplitter extends Thread {
376     final Connection connection;
377     Throwable ex;
378     Table table;
379     TableName tableName;
380     byte[] family;
381     Admin admin;
382     HRegionServer rs;
383 
384     RegionSplitter(Table table) throws IOException {
385       this.table = table;
386       this.tableName = table.getName();
387       this.family = table.getTableDescriptor().getFamiliesKeys().iterator().next();
388       admin = TEST_UTIL.getHBaseAdmin();
389       rs = TEST_UTIL.getMiniHBaseCluster().getRegionServer(0);
390       connection = TEST_UTIL.getConnection();
391     }
392 
393     @Override
394     public void run() {
395       try {
396         Random random = new Random();
397         for (int i= 0; i< 5; i++) {
398           NavigableMap<HRegionInfo, ServerName> regions =
399               MetaScanner.allTableRegions(connection, tableName);
400           if (regions.size() == 0) {
401             continue;
402           }
403           int regionIndex = random.nextInt(regions.size());
404 
405           //pick a random region and split it into two
406           HRegionInfo region = Iterators.get(regions.keySet().iterator(), regionIndex);
407 
408           //pick the mid split point
409           int start = 0, end = Integer.MAX_VALUE;
410           if (region.getStartKey().length > 0) {
411             start = Bytes.toInt(region.getStartKey());
412           }
413           if (region.getEndKey().length > 0) {
414             end = Bytes.toInt(region.getEndKey());
415           }
416           int mid = start + ((end - start) / 2);
417           byte[] splitPoint = Bytes.toBytes(mid);
418 
419           //put some rows to the regions
420           addData(start);
421           addData(mid);
422 
423           flushAndBlockUntilDone(admin, rs, region.getRegionName());
424           compactAndBlockUntilDone(admin, rs, region.getRegionName());
425 
426           log("Initiating region split for:" + region.getRegionNameAsString());
427           try {
428             admin.splitRegion(region.getRegionName(), splitPoint);
429             //wait until the split is complete
430             blockUntilRegionSplit(CONF, 50000, region.getRegionName(), true);
431 
432           } catch (NotServingRegionException ex) {
433             //ignore
434           }
435         }
436       } catch (Throwable ex) {
437         this.ex = ex;
438       }
439     }
440 
441     void addData(int start) throws IOException {
442       List<Put> puts = new ArrayList<>();
443       for (int i=start; i< start + 100; i++) {
444         Put put = new Put(Bytes.toBytes(i));
445         put.addColumn(family, family, Bytes.toBytes(i));
446         puts.add(put);
447       }
448       table.put(puts);
449     }
450   }
451 
452   /**
453    * Checks regions using MetaScanner, MetaTableAccessor and HTable methods
454    */
455   static class RegionChecker extends ScheduledChore {
456     Connection connection;
457     Configuration conf;
458     TableName tableName;
459     Throwable ex;
460 
461     RegionChecker(Configuration conf, Stoppable stopper, TableName tableName) throws IOException {
462       super("RegionChecker", stopper, 100);
463       this.conf = conf;
464       this.tableName = tableName;
465 
466       this.connection = ConnectionFactory.createConnection(conf);
467     }
468 
469     /** verify region boundaries obtained from MetaScanner */
470     void verifyRegionsUsingMetaScanner() throws Exception {
471 
472       //MetaScanner.allTableRegions()
473       NavigableMap<HRegionInfo, ServerName> regions = MetaScanner.allTableRegions(connection,
474           tableName);
475       verifyTableRegions(regions.keySet());
476 
477       //MetaScanner.listAllRegions()
478       List<HRegionInfo> regionList = MetaScanner.listAllRegions(conf, connection, false);
479       verifyTableRegions(Sets.newTreeSet(regionList));
480     }
481 
482     /** verify region boundaries obtained from HTable.getStartEndKeys() */
483     void verifyRegionsUsingHTable() throws IOException {
484       HTable table = null;
485       try {
486         table = (HTable) connection.getTable(tableName);
487         Pair<byte[][], byte[][]> keys = table.getRegionLocator().getStartEndKeys();
488         verifyStartEndKeys(keys);
489 
490         //HTable.getRegionsInfo()
491         Set<HRegionInfo> regions = new TreeSet<HRegionInfo>();
492         for (HRegionLocation loc : table.getRegionLocator().getAllRegionLocations()) {
493           regions.add(loc.getRegionInfo());
494         }
495         verifyTableRegions(regions);
496       } finally {
497         IOUtils.closeQuietly(table);
498       }
499     }
500 
501     void verify() throws Exception {
502       verifyRegionsUsingMetaScanner();
503       verifyRegionsUsingHTable();
504     }
505 
506     void verifyTableRegions(Set<HRegionInfo> regions) {
507       log("Verifying " + regions.size() + " regions: " + regions);
508 
509       byte[][] startKeys = new byte[regions.size()][];
510       byte[][] endKeys = new byte[regions.size()][];
511 
512       int i=0;
513       for (HRegionInfo region : regions) {
514         startKeys[i] = region.getStartKey();
515         endKeys[i] = region.getEndKey();
516         i++;
517       }
518 
519       Pair<byte[][], byte[][]> keys = new Pair<byte[][], byte[][]>(startKeys, endKeys);
520       verifyStartEndKeys(keys);
521     }
522 
523     void verifyStartEndKeys(Pair<byte[][], byte[][]> keys) {
524       byte[][] startKeys = keys.getFirst();
525       byte[][] endKeys = keys.getSecond();
526       assertEquals(startKeys.length, endKeys.length);
527       assertTrue("Found 0 regions for the table", startKeys.length > 0);
528 
529       assertArrayEquals("Start key for the first region is not byte[0]",
530           HConstants.EMPTY_START_ROW, startKeys[0]);
531       byte[] prevEndKey = HConstants.EMPTY_START_ROW;
532 
533       // ensure that we do not have any gaps
534       for (int i=0; i<startKeys.length; i++) {
535         assertArrayEquals(
536             "Hole in hbase:meta is detected. prevEndKey=" + Bytes.toStringBinary(prevEndKey)
537                 + " ,regionStartKey=" + Bytes.toStringBinary(startKeys[i]), prevEndKey,
538             startKeys[i]);
539         prevEndKey = endKeys[i];
540       }
541       assertArrayEquals("End key for the last region is not byte[0]", HConstants.EMPTY_END_ROW,
542           endKeys[endKeys.length - 1]);
543     }
544 
545     @Override
546     protected void chore() {
547       try {
548         verify();
549       } catch (Throwable ex) {
550         this.ex = ex;
551         getStopper().stop("caught exception");
552       }
553     }
554   }
555 
556   public static void log(String msg) {
557     LOG.info(msg);
558   }
559 
560   /* some utility methods for split tests */
561 
562   public static void flushAndBlockUntilDone(Admin admin, HRegionServer rs, byte[] regionName)
563       throws IOException, InterruptedException {
564     log("flushing region: " + Bytes.toStringBinary(regionName));
565     admin.flushRegion(regionName);
566     log("blocking until flush is complete: " + Bytes.toStringBinary(regionName));
567     Threads.sleepWithoutInterrupt(500);
568     while (rs.getOnlineRegion(regionName).getMemstoreSize() > 0) {
569       Threads.sleep(50);
570     }
571   }
572 
573   public static void compactAndBlockUntilDone(Admin admin, HRegionServer rs, byte[] regionName)
574       throws IOException, InterruptedException {
575     log("Compacting region: " + Bytes.toStringBinary(regionName));
576     admin.majorCompactRegion(regionName);
577     log("blocking until compaction is complete: " + Bytes.toStringBinary(regionName));
578     Threads.sleepWithoutInterrupt(500);
579     outer: for (;;) {
580       for (Store store : rs.getOnlineRegion(regionName).getStores()) {
581         if (store.getStorefilesCount() > 1) {
582           Threads.sleep(50);
583           continue outer;
584         }
585       }
586       break;
587     }
588   }
589 
590   /** Blocks until the region split is complete in hbase:meta and region server opens the daughters */
591   public static void blockUntilRegionSplit(Configuration conf, long timeout,
592       final byte[] regionName, boolean waitForDaughters)
593       throws IOException, InterruptedException {
594     long start = System.currentTimeMillis();
595     log("blocking until region is split:" +  Bytes.toStringBinary(regionName));
596     HRegionInfo daughterA = null, daughterB = null;
597     try (Connection conn = ConnectionFactory.createConnection(conf);
598         Table metaTable = conn.getTable(TableName.META_TABLE_NAME)) {
599       Result result = null;
600       HRegionInfo region = null;
601       while ((System.currentTimeMillis() - start) < timeout) {
602         result = metaTable.get(new Get(regionName));
603         if (result == null) {
604           break;
605         }
606 
607         region = MetaTableAccessor.getHRegionInfo(result);
608         if (region.isSplitParent()) {
609           log("found parent region: " + region.toString());
610           PairOfSameType<HRegionInfo> pair = MetaTableAccessor.getDaughterRegions(result);
611           daughterA = pair.getFirst();
612           daughterB = pair.getSecond();
613           break;
614         }
615         Threads.sleep(100);
616       }
617       if (daughterA == null || daughterB == null) {
618         throw new IOException("Failed to get daughters, daughterA=" + daughterA + ", daughterB=" +
619           daughterB + ", timeout=" + timeout + ", result=" + result + ", regionName=" +
620           Bytes.toString(regionName) + ", region=" + region);
621       }
622 
623       //if we are here, this means the region split is complete or timed out
624       if (waitForDaughters) {
625         long rem = timeout - (System.currentTimeMillis() - start);
626         blockUntilRegionIsInMeta(conn, rem, daughterA);
627 
628         rem = timeout - (System.currentTimeMillis() - start);
629         blockUntilRegionIsInMeta(conn, rem, daughterB);
630 
631         rem = timeout - (System.currentTimeMillis() - start);
632         blockUntilRegionIsOpened(conf, rem, daughterA);
633 
634         rem = timeout - (System.currentTimeMillis() - start);
635         blockUntilRegionIsOpened(conf, rem, daughterB);
636 
637         // Compacting the new region to make sure references can be cleaned up
638         compactAndBlockUntilDone(TEST_UTIL.getHBaseAdmin(),
639           TEST_UTIL.getMiniHBaseCluster().getRegionServer(0), daughterA.getRegionName());
640         compactAndBlockUntilDone(TEST_UTIL.getHBaseAdmin(),
641           TEST_UTIL.getMiniHBaseCluster().getRegionServer(0), daughterB.getRegionName());
642 
643         removeCompactedfiles(conn, timeout, daughterA);
644         removeCompactedfiles(conn, timeout, daughterB);
645       }
646     }
647   }
648 
649   public static void removeCompactedfiles(Connection conn, long timeout, HRegionInfo hri)
650       throws IOException, InterruptedException {
651     log("removeCompactedfiles for : " + hri.getRegionNameAsString());
652     List<HRegion> regions = TEST_UTIL.getHBaseCluster().getRegions(hri.getTable());
653     for (HRegion region : regions) {
654       try {
655         region.getStores().get(0).closeAndArchiveCompactedFiles();
656       } catch (IOException e) {
657         e.printStackTrace();
658       }
659     }
660   }
661 
662   public static void blockUntilRegionIsInMeta(Connection conn, long timeout, HRegionInfo hri)
663       throws IOException, InterruptedException {
664     log("blocking until region is in META: " + hri.getRegionNameAsString());
665     long start = System.currentTimeMillis();
666     while (System.currentTimeMillis() - start < timeout) {
667       HRegionLocation loc = MetaTableAccessor.getRegionLocation(conn, hri);
668       if (loc != null && !loc.getRegionInfo().isOffline()) {
669         log("found region in META: " + hri.getRegionNameAsString());
670         break;
671       }
672       Threads.sleep(100);
673     }
674   }
675 
676   public static void blockUntilRegionIsOpened(Configuration conf, long timeout, HRegionInfo hri)
677       throws IOException, InterruptedException {
678     log("blocking until region is opened for reading:" + hri.getRegionNameAsString());
679     long start = System.currentTimeMillis();
680     try (Connection conn = ConnectionFactory.createConnection(conf);
681         Table table = conn.getTable(hri.getTable())) {
682       byte[] row = hri.getStartKey();
683       // Check for null/empty row. If we find one, use a key that is likely to be in first region.
684       if (row == null || row.length <= 0) row = new byte[] { '0' };
685       Get get = new Get(row);
686       while (System.currentTimeMillis() - start < timeout) {
687         try {
688           table.get(get);
689           break;
690         } catch (IOException ex) {
691           // wait some more
692         }
693         Threads.sleep(100);
694       }
695     }
696   }
697 }
698