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.client;
019
020import static org.junit.jupiter.api.Assertions.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertFalse;
022import static org.junit.jupiter.api.Assertions.assertNotNull;
023import static org.junit.jupiter.api.Assertions.assertThrows;
024import static org.junit.jupiter.api.Assertions.assertTrue;
025import static org.junit.jupiter.api.Assertions.fail;
026
027import java.io.IOException;
028import java.util.ArrayList;
029import java.util.List;
030import java.util.concurrent.ExecutionException;
031import java.util.concurrent.TimeUnit;
032import java.util.concurrent.atomic.AtomicInteger;
033import org.apache.hadoop.hbase.HConstants;
034import org.apache.hadoop.hbase.HRegionLocation;
035import org.apache.hadoop.hbase.MetaTableAccessor;
036import org.apache.hadoop.hbase.ServerName;
037import org.apache.hadoop.hbase.TableName;
038import org.apache.hadoop.hbase.TableNotFoundException;
039import org.apache.hadoop.hbase.exceptions.MergeRegionException;
040import org.apache.hadoop.hbase.master.HMaster;
041import org.apache.hadoop.hbase.master.janitor.CatalogJanitor;
042import org.apache.hadoop.hbase.regionserver.DisabledRegionSplitPolicy;
043import org.apache.hadoop.hbase.regionserver.HRegion;
044import org.apache.hadoop.hbase.regionserver.HStore;
045import org.apache.hadoop.hbase.regionserver.HStoreFile;
046import org.apache.hadoop.hbase.testclassification.ClientTests;
047import org.apache.hadoop.hbase.testclassification.LargeTests;
048import org.apache.hadoop.hbase.util.Bytes;
049import org.apache.hadoop.hbase.util.CommonFSUtils;
050import org.apache.hadoop.hbase.util.FutureUtils;
051import org.apache.hadoop.hbase.util.Pair;
052import org.apache.hadoop.hbase.util.Threads;
053import org.junit.jupiter.api.Tag;
054import org.junit.jupiter.api.Test;
055import org.slf4j.Logger;
056import org.slf4j.LoggerFactory;
057
058import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter;
059import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.MergeTableRegionsRequest;
060
061/**
062 * Class to test HBaseAdmin. Spins up the minicluster once at test start and then takes it down
063 * afterward. Add any testing of HBaseAdmin functionality here.
064 */
065@Tag(LargeTests.TAG)
066@Tag(ClientTests.TAG)
067public class TestAdmin1 extends TestAdminBase {
068
069  private static final Logger LOG = LoggerFactory.getLogger(TestAdmin1.class);
070
071  @Test
072  public void testSplitFlushCompactUnknownTable() throws InterruptedException {
073    final TableName unknowntable = TableName.valueOf(methodName);
074    Exception exception = null;
075    try {
076      ADMIN.compact(unknowntable);
077    } catch (IOException e) {
078      exception = e;
079    }
080    assertTrue(exception instanceof TableNotFoundException);
081
082    exception = null;
083    try {
084      ADMIN.flush(unknowntable);
085    } catch (IOException e) {
086      exception = e;
087    }
088    assertTrue(exception instanceof TableNotFoundException);
089
090    exception = null;
091    try {
092      ADMIN.split(unknowntable);
093    } catch (IOException e) {
094      exception = e;
095    }
096    assertTrue(exception instanceof TableNotFoundException);
097  }
098
099  @Test
100  public void testCompactATableWithSuperLongTableName() throws Exception {
101    TableName tableName = TableName.valueOf(methodName);
102    TableDescriptor htd = TableDescriptorBuilder.newBuilder(tableName)
103      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("fam1")).build();
104    try {
105      ADMIN.createTable(htd);
106      assertThrows(IllegalArgumentException.class,
107        () -> ADMIN.majorCompactRegion(tableName.getName()));
108
109      assertThrows(IllegalArgumentException.class,
110        () -> ADMIN.majorCompactRegion(Bytes.toBytes("abcd")));
111    } finally {
112      ADMIN.disableTable(tableName);
113      ADMIN.deleteTable(tableName);
114    }
115  }
116
117  @Test
118  public void testCompactionTimestamps() throws Exception {
119    TableName tableName = TableName.valueOf(methodName);
120    TableDescriptor htd = TableDescriptorBuilder.newBuilder(tableName)
121      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("fam1")).build();
122    ADMIN.createTable(htd);
123    Table table = TEST_UTIL.getConnection().getTable(htd.getTableName());
124    long ts = ADMIN.getLastMajorCompactionTimestamp(tableName);
125    assertEquals(0, ts);
126    Put p = new Put(Bytes.toBytes("row1"));
127    p.addColumn(Bytes.toBytes("fam1"), Bytes.toBytes("fam1"), Bytes.toBytes("fam1"));
128    table.put(p);
129    ts = ADMIN.getLastMajorCompactionTimestamp(tableName);
130    // no files written -> no data
131    assertEquals(0, ts);
132
133    ADMIN.flush(tableName);
134    ts = ADMIN.getLastMajorCompactionTimestamp(tableName);
135    // still 0, we flushed a file, but no major compaction happened
136    assertEquals(0, ts);
137
138    byte[] regionName;
139    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
140      regionName = l.getAllRegionLocations().get(0).getRegion().getRegionName();
141    }
142    long ts1 = ADMIN.getLastMajorCompactionTimestampForRegion(regionName);
143    assertEquals(ts, ts1);
144    p = new Put(Bytes.toBytes("row2"));
145    p.addColumn(Bytes.toBytes("fam1"), Bytes.toBytes("fam1"), Bytes.toBytes("fam1"));
146    table.put(p);
147    ADMIN.flush(tableName);
148    ts = ADMIN.getLastMajorCompactionTimestamp(tableName);
149    // make sure the region API returns the same value, as the old file is still around
150    assertEquals(ts1, ts);
151
152    TEST_UTIL.compact(tableName, true);
153    table.put(p);
154    // forces a wait for the compaction
155    ADMIN.flush(tableName);
156    ts = ADMIN.getLastMajorCompactionTimestamp(tableName);
157    // after a compaction our earliest timestamp will have progressed forward
158    assertTrue(ts > ts1);
159
160    // region api still the same
161    ts1 = ADMIN.getLastMajorCompactionTimestampForRegion(regionName);
162    assertEquals(ts, ts1);
163    table.put(p);
164    ADMIN.flush(tableName);
165    ts = ADMIN.getLastMajorCompactionTimestamp(tableName);
166    assertEquals(ts, ts1);
167    table.close();
168  }
169
170  @Test
171  public void testColumnValidName() {
172    assertThrows(IllegalArgumentException.class,
173      () -> ColumnFamilyDescriptorBuilder.of("\\test\\abc"));
174  }
175
176  @Test
177  public void testTableExist() throws IOException {
178    final TableName table = TableName.valueOf(methodName);
179    boolean exist;
180    exist = ADMIN.tableExists(table);
181    assertEquals(false, exist);
182    TEST_UTIL.createTable(table, HConstants.CATALOG_FAMILY);
183    exist = ADMIN.tableExists(table);
184    assertEquals(true, exist);
185  }
186
187  /**
188   * Tests forcing split from client and having scanners successfully ride over split.
189   */
190  @Test
191  public void testForceSplit() throws Exception {
192    byte[][] familyNames = new byte[][] { Bytes.toBytes("cf") };
193    int[] rowCounts = new int[] { 6000 };
194    int numVersions = ColumnFamilyDescriptorBuilder.DEFAULT_MAX_VERSIONS;
195    int blockSize = 256;
196    splitTest(null, familyNames, rowCounts, numVersions, blockSize, true);
197
198    byte[] splitKey = Bytes.toBytes(3500);
199    splitTest(splitKey, familyNames, rowCounts, numVersions, blockSize, true);
200    // test regionSplitSync
201    splitTest(splitKey, familyNames, rowCounts, numVersions, blockSize, false);
202  }
203
204  /**
205   * Multi-family scenario. Tests forcing split from client and having scanners successfully ride
206   * over split.
207   */
208  @Test
209  public void testForceSplitMultiFamily() throws Exception {
210    int numVersions = ColumnFamilyDescriptorBuilder.DEFAULT_MAX_VERSIONS;
211
212    // use small HFile block size so that we can have lots of blocks in HFile
213    // Otherwise, if there is only one block,
214    // HFileBlockIndex.midKey()'s value == startKey
215    int blockSize = 256;
216    byte[][] familyNames = new byte[][] { Bytes.toBytes("cf1"), Bytes.toBytes("cf2") };
217
218    // one of the column families isn't splittable
219    int[] rowCounts = new int[] { 6000, 1 };
220    splitTest(null, familyNames, rowCounts, numVersions, blockSize, true);
221
222    rowCounts = new int[] { 1, 6000 };
223    splitTest(null, familyNames, rowCounts, numVersions, blockSize, true);
224
225    // one column family has much smaller data than the other
226    // the split key should be based on the largest column family
227    rowCounts = new int[] { 6000, 300 };
228    splitTest(null, familyNames, rowCounts, numVersions, blockSize, true);
229
230    rowCounts = new int[] { 300, 6000 };
231    splitTest(null, familyNames, rowCounts, numVersions, blockSize, true);
232  }
233
234  private int count(ResultScanner scanner) throws IOException {
235    int rows = 0;
236    while (scanner.next() != null) {
237      rows++;
238    }
239    return rows;
240  }
241
242  private void splitTest(byte[] splitPoint, byte[][] familyNames, int[] rowCounts, int numVersions,
243    int blockSize, boolean async) throws Exception {
244    TableName tableName = TableName.valueOf("testForceSplit");
245    StringBuilder sb = new StringBuilder();
246    // Add tail to String so can see better in logs where a test is running.
247    for (int i = 0; i < rowCounts.length; i++) {
248      sb.append("_").append(Integer.toString(rowCounts[i]));
249    }
250    assertFalse(ADMIN.tableExists(tableName));
251    try (final Table table = TEST_UTIL.createTable(tableName, familyNames, numVersions, blockSize);
252      final RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
253
254      int rowCount = 0;
255      byte[] q = new byte[0];
256
257      // insert rows into column families. The number of rows that have values
258      // in a specific column family is decided by rowCounts[familyIndex]
259      for (int index = 0; index < familyNames.length; index++) {
260        ArrayList<Put> puts = new ArrayList<>(rowCounts[index]);
261        for (int i = 0; i < rowCounts[index]; i++) {
262          byte[] k = Bytes.toBytes(i);
263          Put put = new Put(k);
264          put.addColumn(familyNames[index], q, k);
265          puts.add(put);
266        }
267        table.put(puts);
268
269        if (rowCount < rowCounts[index]) {
270          rowCount = rowCounts[index];
271        }
272      }
273
274      // get the initial layout (should just be one region)
275      List<HRegionLocation> m = locator.getAllRegionLocations();
276      LOG.info("Initial regions (" + m.size() + "): " + m);
277      assertTrue(m.size() == 1);
278
279      // Verify row count
280      Scan scan = new Scan();
281      int rows;
282      try (ResultScanner scanner = table.getScanner(scan)) {
283        rows = count(scanner);
284      }
285      assertEquals(rowCount, rows);
286
287      // Have an outstanding scan going on to make sure we can scan over splits.
288      scan = new Scan();
289      try (ResultScanner scanner = table.getScanner(scan)) {
290        // Scan first row so we are into first region before split happens.
291        scanner.next();
292
293        // Split the table
294        if (async) {
295          if (splitPoint != null) {
296            ADMIN.split(tableName, splitPoint);
297          } else {
298            ADMIN.split(tableName);
299          }
300          final AtomicInteger count = new AtomicInteger(0);
301          Thread t = new Thread("CheckForSplit") {
302            @Override
303            public void run() {
304              for (int i = 0; i < 45; i++) {
305                try {
306                  sleep(1000);
307                } catch (InterruptedException e) {
308                  continue;
309                }
310                // check again
311                List<HRegionLocation> regions = null;
312                try {
313                  regions = locator.getAllRegionLocations();
314                } catch (IOException e) {
315                  LOG.warn("get location failed", e);
316                }
317                if (regions == null) {
318                  continue;
319                }
320                count.set(regions.size());
321                if (count.get() >= 2) {
322                  LOG.info("Found: " + regions);
323                  break;
324                }
325                LOG.debug("Cycle waiting on split");
326              }
327              LOG.debug("CheckForSplit thread exited, current region count: " + count.get());
328            }
329          };
330          t.setPriority(Thread.NORM_PRIORITY - 2);
331          t.start();
332          t.join();
333        } else {
334          // Sync split region, no need to create a thread to check
335          ADMIN.splitRegionAsync(m.get(0).getRegion().getRegionName(), splitPoint).get();
336        }
337        // Verify row count
338        rows = 1 + count(scanner); // We counted one row above.
339      }
340      assertEquals(rowCount, rows);
341
342      List<HRegionLocation> regions = null;
343      try {
344        regions = locator.getAllRegionLocations();
345      } catch (IOException e) {
346        e.printStackTrace();
347      }
348      assertEquals(2, regions.size());
349      if (splitPoint != null) {
350        // make sure the split point matches our explicit configuration
351        assertEquals(Bytes.toString(splitPoint),
352          Bytes.toString(regions.get(0).getRegion().getEndKey()));
353        assertEquals(Bytes.toString(splitPoint),
354          Bytes.toString(regions.get(1).getRegion().getStartKey()));
355        LOG.debug("Properly split on " + Bytes.toString(splitPoint));
356      } else {
357        if (familyNames.length > 1) {
358          int splitKey = Bytes.toInt(regions.get(0).getRegion().getEndKey());
359          // check if splitKey is based on the largest column family
360          // in terms of it store size
361          int deltaForLargestFamily = Math.abs(rowCount / 2 - splitKey);
362          LOG.debug("SplitKey=" + splitKey + "&deltaForLargestFamily=" + deltaForLargestFamily
363            + ", r=" + regions.get(0).getRegion());
364          for (int index = 0; index < familyNames.length; index++) {
365            int delta = Math.abs(rowCounts[index] / 2 - splitKey);
366            if (delta < deltaForLargestFamily) {
367              assertTrue(false, "Delta " + delta + " for family " + index + " should be at least "
368                + "deltaForLargestFamily " + deltaForLargestFamily);
369            }
370          }
371        }
372      }
373      TEST_UTIL.deleteTable(tableName);
374    }
375  }
376
377  @Test
378  public void testSplitAndMergeWithReplicaTable() throws Exception {
379    // The test tries to directly split replica regions and directly merge replica regions. These
380    // are not allowed. The test validates that. Then the test does a valid split/merge of allowed
381    // regions.
382    // Set up a table with 3 regions and replication set to 3
383    TableName tableName = TableName.valueOf(methodName);
384    byte[] cf = Bytes.toBytes("f");
385    TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName).setRegionReplication(3)
386      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(cf)).build();
387    byte[][] splitRows = new byte[2][];
388    splitRows[0] = new byte[] { (byte) '4' };
389    splitRows[1] = new byte[] { (byte) '7' };
390    TEST_UTIL.getAdmin().createTable(desc, splitRows);
391    List<HRegion> oldRegions;
392    do {
393      oldRegions = TEST_UTIL.getHBaseCluster().getRegions(tableName);
394      Thread.sleep(10);
395    } while (oldRegions.size() != 9); // 3 regions * 3 replicas
396    // write some data to the table
397    Table ht = TEST_UTIL.getConnection().getTable(tableName);
398    List<Put> puts = new ArrayList<>();
399    byte[] qualifier = Bytes.toBytes("c");
400    Put put = new Put(new byte[] { (byte) '1' });
401    put.addColumn(cf, qualifier, Bytes.toBytes("100"));
402    puts.add(put);
403    put = new Put(new byte[] { (byte) '6' });
404    put.addColumn(cf, qualifier, Bytes.toBytes("100"));
405    puts.add(put);
406    put = new Put(new byte[] { (byte) '8' });
407    put.addColumn(cf, qualifier, Bytes.toBytes("100"));
408    puts.add(put);
409    ht.put(puts);
410    ht.close();
411    List<Pair<RegionInfo, ServerName>> regions =
412      MetaTableAccessor.getTableRegionsAndLocations(TEST_UTIL.getConnection(), tableName);
413    boolean gotException = false;
414    // the element at index 1 would be a replica (since the metareader gives us ordered
415    // regions). Try splitting that region via the split API . Should fail
416    try {
417      FutureUtils
418        .get(TEST_UTIL.getAdmin().splitRegionAsync(regions.get(1).getFirst().getRegionName()));
419    } catch (IllegalArgumentException ex) {
420      gotException = true;
421    }
422    assertTrue(gotException);
423    gotException = false;
424    // the element at index 1 would be a replica (since the metareader gives us ordered
425    // regions). Try splitting that region via a different split API (the difference is
426    // this API goes direct to the regionserver skipping any checks in the admin). Should fail
427    try {
428      FutureUtils.get(TEST_UTIL.getAdmin().splitRegionAsync(
429        regions.get(1).getFirst().getEncodedNameAsBytes(), new byte[] { (byte) '1' }));
430    } catch (IllegalArgumentException ex) {
431      gotException = true;
432    }
433    assertTrue(gotException);
434
435    gotException = false;
436    // testing Sync split operation
437    try {
438      FutureUtils.get(TEST_UTIL.getAdmin()
439        .splitRegionAsync(regions.get(1).getFirst().getRegionName(), new byte[] { (byte) '1' }));
440    } catch (IllegalArgumentException ex) {
441      gotException = true;
442    }
443    assertTrue(gotException);
444
445    gotException = false;
446    // Try merging a replica with another. Should fail.
447    try {
448      FutureUtils.get(
449        TEST_UTIL.getAdmin().mergeRegionsAsync(regions.get(1).getFirst().getEncodedNameAsBytes(),
450          regions.get(2).getFirst().getEncodedNameAsBytes(), true));
451    } catch (IllegalArgumentException m) {
452      gotException = true;
453    }
454    assertTrue(gotException);
455    // Try going to the master directly (that will skip the check in admin)
456    try {
457      byte[][] nameofRegionsToMerge = new byte[2][];
458      nameofRegionsToMerge[0] = regions.get(1).getFirst().getEncodedNameAsBytes();
459      nameofRegionsToMerge[1] = regions.get(2).getFirst().getEncodedNameAsBytes();
460      MergeTableRegionsRequest request = RequestConverter.buildMergeTableRegionsRequest(
461        nameofRegionsToMerge, true, HConstants.NO_NONCE, HConstants.NO_NONCE);
462      TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterRpcServices().mergeTableRegions(null,
463        request);
464    } catch (org.apache.hbase.thirdparty.com.google.protobuf.ServiceException m) {
465      Throwable t = m.getCause();
466      do {
467        if (t instanceof MergeRegionException) {
468          gotException = true;
469          break;
470        }
471        t = t.getCause();
472      } while (t != null);
473    }
474    assertTrue(gotException);
475  }
476
477  @Test
478  public void testInvalidColumnDescriptor() {
479    assertThrows(IllegalArgumentException.class,
480      () -> ColumnFamilyDescriptorBuilder.of("/cfamily/name"));
481  }
482
483  /**
484   * Test DFS replication for column families, where one CF has default replication(3) and the other
485   * is set to 1.
486   */
487  @Test
488  public void testHFileReplication() throws Exception {
489    final TableName tableName = TableName.valueOf(methodName);
490    String fn1 = "rep1";
491    String fn = "defaultRep";
492    TableDescriptor htd = TableDescriptorBuilder.newBuilder(tableName)
493      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(fn))
494      .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(fn1))
495        .setDFSReplication((short) 1).build())
496      .build();
497    Table table = TEST_UTIL.createTable(htd, null);
498    TEST_UTIL.waitTableAvailable(tableName);
499    Put p = new Put(Bytes.toBytes("defaultRep_rk"));
500    byte[] q1 = Bytes.toBytes("q1");
501    byte[] v1 = Bytes.toBytes("v1");
502    p.addColumn(Bytes.toBytes(fn), q1, v1);
503    List<Put> puts = new ArrayList<>(2);
504    puts.add(p);
505    p = new Put(Bytes.toBytes("rep1_rk"));
506    p.addColumn(Bytes.toBytes(fn1), q1, v1);
507    puts.add(p);
508    try {
509      table.put(puts);
510      ADMIN.flush(tableName);
511
512      List<HRegion> regions = TEST_UTIL.getMiniHBaseCluster().getRegions(tableName);
513      for (HRegion r : regions) {
514        HStore store = r.getStore(Bytes.toBytes(fn));
515        for (HStoreFile sf : store.getStorefiles()) {
516          assertTrue(sf.toString().contains(fn));
517          assertTrue(
518            CommonFSUtils.getDefaultReplication(TEST_UTIL.getTestFileSystem(), sf.getPath())
519                == (sf.getFileInfo().getFileStatus().getReplication()),
520            "Column family " + fn + " should have 3 copies");
521        }
522
523        store = r.getStore(Bytes.toBytes(fn1));
524        for (HStoreFile sf : store.getStorefiles()) {
525          assertTrue(sf.toString().contains(fn1));
526          assertTrue(1 == sf.getFileInfo().getFileStatus().getReplication(),
527            "Column family " + fn1 + " should have only 1 copy");
528        }
529      }
530    } finally {
531      if (ADMIN.isTableEnabled(tableName)) {
532        ADMIN.disableTable(tableName);
533        ADMIN.deleteTable(tableName);
534      }
535    }
536  }
537
538  @Test
539  public void testMergeRegions() throws Exception {
540    final TableName tableName = TableName.valueOf(methodName);
541    TableDescriptor td = TableDescriptorBuilder.newBuilder(tableName)
542      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("d")).build();
543    byte[][] splitRows = new byte[2][];
544    splitRows[0] = new byte[] { (byte) '3' };
545    splitRows[1] = new byte[] { (byte) '6' };
546    try {
547      TEST_UTIL.createTable(td, splitRows);
548      TEST_UTIL.waitTableAvailable(tableName);
549
550      List<RegionInfo> tableRegions;
551      RegionInfo regionA;
552      RegionInfo regionB;
553      RegionInfo regionC;
554      RegionInfo mergedChildRegion = null;
555
556      // merge with full name
557      tableRegions = ADMIN.getRegions(tableName);
558      assertEquals(3, ADMIN.getRegions(tableName).size());
559      regionA = tableRegions.get(0);
560      regionB = tableRegions.get(1);
561      regionC = tableRegions.get(2);
562      // TODO convert this to version that is synchronous (See HBASE-16668)
563      ADMIN.mergeRegionsAsync(regionA.getRegionName(), regionB.getRegionName(), false).get(60,
564        TimeUnit.SECONDS);
565
566      tableRegions = ADMIN.getRegions(tableName);
567
568      assertEquals(2, tableRegions.size());
569      for (RegionInfo ri : tableRegions) {
570        if (regionC.compareTo(ri) != 0) {
571          mergedChildRegion = ri;
572          break;
573        }
574      }
575
576      assertNotNull(mergedChildRegion);
577      // Need to wait GC for merged child region is done.
578      HMaster services = TEST_UTIL.getHBaseCluster().getMaster();
579      CatalogJanitor cj = services.getCatalogJanitor();
580      assertTrue(cj.scan() > 0);
581      // Wait until all procedures settled down
582      while (!services.getMasterProcedureExecutor().getActiveProcIds().isEmpty()) {
583        Thread.sleep(200);
584      }
585
586      // TODO convert this to version that is synchronous (See HBASE-16668)
587      ADMIN.mergeRegionsAsync(regionC.getEncodedNameAsBytes(),
588        mergedChildRegion.getEncodedNameAsBytes(), false).get(60, TimeUnit.SECONDS);
589
590      assertEquals(1, ADMIN.getRegions(tableName).size());
591    } finally {
592      ADMIN.disableTable(tableName);
593      ADMIN.deleteTable(tableName);
594    }
595  }
596
597  @Test
598  public void testMergeRegionsInvalidRegionCount()
599    throws IOException, InterruptedException, ExecutionException {
600    TableName tableName = TableName.valueOf(methodName);
601    TableDescriptor td = TableDescriptorBuilder.newBuilder(tableName)
602      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("d")).build();
603    byte[][] splitRows = new byte[2][];
604    splitRows[0] = new byte[] { (byte) '3' };
605    splitRows[1] = new byte[] { (byte) '6' };
606    try {
607      TEST_UTIL.createTable(td, splitRows);
608      TEST_UTIL.waitTableAvailable(tableName);
609
610      List<RegionInfo> tableRegions = ADMIN.getRegions(tableName);
611      // 0
612      try {
613        FutureUtils.get(ADMIN.mergeRegionsAsync(new byte[0][0], false));
614        fail();
615      } catch (IllegalArgumentException e) {
616        // expected
617      }
618      // 1
619      try {
620        FutureUtils.get(ADMIN
621          .mergeRegionsAsync(new byte[][] { tableRegions.get(0).getEncodedNameAsBytes() }, false));
622        fail();
623      } catch (IllegalArgumentException e) {
624        // expected
625      }
626    } finally {
627      ADMIN.disableTable(tableName);
628      ADMIN.deleteTable(tableName);
629    }
630  }
631
632  @Test
633  public void testSplitShouldNotHappenIfSplitIsDisabledForTable() throws Exception {
634    final TableName tableName = TableName.valueOf(methodName);
635    TableDescriptor htd = TableDescriptorBuilder.newBuilder(tableName)
636      .setRegionSplitPolicyClassName(DisabledRegionSplitPolicy.class.getName())
637      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("f")).build();
638    Table table = TEST_UTIL.createTable(htd, null);
639    for (int i = 0; i < 10; i++) {
640      Put p = new Put(Bytes.toBytes("row" + i));
641      byte[] q1 = Bytes.toBytes("q1");
642      byte[] v1 = Bytes.toBytes("v1");
643      p.addColumn(Bytes.toBytes("f"), q1, v1);
644      table.put(p);
645    }
646    ADMIN.flush(tableName);
647    try {
648      ADMIN.split(tableName, Bytes.toBytes("row5"));
649      Threads.sleep(10000);
650    } catch (Exception e) {
651      // Nothing to do.
652    }
653    // Split should not happen.
654    List<RegionInfo> allRegions =
655      MetaTableAccessor.getTableRegions(ADMIN.getConnection(), tableName, true);
656    assertEquals(1, allRegions.size());
657  }
658}