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