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.HashMap;
029import java.util.Iterator;
030import java.util.List;
031import java.util.Map;
032import java.util.concurrent.TimeUnit;
033import java.util.concurrent.atomic.AtomicInteger;
034import org.apache.hadoop.hbase.HBaseClassTestRule;
035import org.apache.hadoop.hbase.HBaseTestingUtility;
036import org.apache.hadoop.hbase.HColumnDescriptor;
037import org.apache.hadoop.hbase.HConstants;
038import org.apache.hadoop.hbase.HRegionLocation;
039import org.apache.hadoop.hbase.HTableDescriptor;
040import org.apache.hadoop.hbase.InvalidFamilyOperationException;
041import org.apache.hadoop.hbase.MetaTableAccessor;
042import org.apache.hadoop.hbase.ServerName;
043import org.apache.hadoop.hbase.TableName;
044import org.apache.hadoop.hbase.TableNotDisabledException;
045import org.apache.hadoop.hbase.TableNotEnabledException;
046import org.apache.hadoop.hbase.TableNotFoundException;
047import org.apache.hadoop.hbase.exceptions.MergeRegionException;
048import org.apache.hadoop.hbase.master.LoadBalancer;
049import org.apache.hadoop.hbase.regionserver.DisabledRegionSplitPolicy;
050import org.apache.hadoop.hbase.regionserver.HRegion;
051import org.apache.hadoop.hbase.regionserver.HStore;
052import org.apache.hadoop.hbase.regionserver.HStoreFile;
053import org.apache.hadoop.hbase.testclassification.ClientTests;
054import org.apache.hadoop.hbase.testclassification.LargeTests;
055import org.apache.hadoop.hbase.util.Bytes;
056import org.apache.hadoop.hbase.util.FSUtils;
057import org.apache.hadoop.hbase.util.Pair;
058import org.apache.hadoop.hbase.util.Threads;
059import org.junit.After;
060import org.junit.AfterClass;
061import org.junit.Before;
062import org.junit.BeforeClass;
063import org.junit.ClassRule;
064import org.junit.Rule;
065import org.junit.Test;
066import org.junit.experimental.categories.Category;
067import org.junit.rules.TestName;
068import org.slf4j.Logger;
069import org.slf4j.LoggerFactory;
070
071import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter;
072import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.MergeTableRegionsRequest;
073
074/**
075 * Class to test HBaseAdmin.
076 * Spins up the minicluster once at test start and then takes it down afterward.
077 * Add any testing of HBaseAdmin functionality here.
078 */
079@Category({LargeTests.class, ClientTests.class})
080public class TestAdmin1 {
081
082  @ClassRule
083  public static final HBaseClassTestRule CLASS_RULE =
084      HBaseClassTestRule.forClass(TestAdmin1.class);
085
086  private static final Logger LOG = LoggerFactory.getLogger(TestAdmin1.class);
087  private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
088  private Admin admin;
089
090  @Rule
091  public TestName name = new TestName();
092
093  @BeforeClass
094  public static void setUpBeforeClass() throws Exception {
095    TEST_UTIL.getConfiguration().setInt("hbase.regionserver.msginterval", 100);
096    TEST_UTIL.getConfiguration().setInt("hbase.client.pause", 250);
097    TEST_UTIL.getConfiguration().setInt("hbase.client.retries.number", 6);
098    TEST_UTIL.getConfiguration().setBoolean("hbase.master.enabletable.roundrobin", true);
099    TEST_UTIL.startMiniCluster(3);
100  }
101
102  @AfterClass
103  public static void tearDownAfterClass() throws Exception {
104    TEST_UTIL.shutdownMiniCluster();
105  }
106
107  @Before
108  public void setUp() throws Exception {
109    this.admin = TEST_UTIL.getAdmin();
110  }
111
112  @After
113  public void tearDown() throws Exception {
114    for (HTableDescriptor htd : this.admin.listTables()) {
115      TEST_UTIL.deleteTable(htd.getTableName());
116    }
117  }
118
119  @Test
120  public void testSplitFlushCompactUnknownTable() throws InterruptedException {
121    final TableName unknowntable = TableName.valueOf(name.getMethodName());
122    Exception exception = null;
123    try {
124      this.admin.compact(unknowntable);
125    } catch (IOException e) {
126      exception = e;
127    }
128    assertTrue(exception instanceof TableNotFoundException);
129
130    exception = null;
131    try {
132      this.admin.flush(unknowntable);
133    } catch (IOException e) {
134      exception = e;
135    }
136    assertTrue(exception instanceof TableNotFoundException);
137
138    exception = null;
139    try {
140      this.admin.split(unknowntable);
141    } catch (IOException e) {
142      exception = e;
143    }
144    assertTrue(exception instanceof TableNotFoundException);
145  }
146
147  @Test
148  public void testDeleteEditUnknownColumnFamilyAndOrTable() throws IOException {
149    // Test we get exception if we try to
150    final TableName nonexistentTable = TableName.valueOf("nonexistent");
151    final byte[] nonexistentColumn = Bytes.toBytes("nonexistent");
152    HColumnDescriptor nonexistentHcd = new HColumnDescriptor(nonexistentColumn);
153    Exception exception = null;
154    try {
155      this.admin.addColumnFamily(nonexistentTable, nonexistentHcd);
156    } catch (IOException e) {
157      exception = e;
158    }
159    assertTrue(exception instanceof TableNotFoundException);
160
161    exception = null;
162    try {
163      this.admin.deleteTable(nonexistentTable);
164    } catch (IOException e) {
165      exception = e;
166    }
167    assertTrue(exception instanceof TableNotFoundException);
168
169    exception = null;
170    try {
171      this.admin.deleteColumnFamily(nonexistentTable, nonexistentColumn);
172    } catch (IOException e) {
173      exception = e;
174    }
175    assertTrue(exception instanceof TableNotFoundException);
176
177    exception = null;
178    try {
179      this.admin.disableTable(nonexistentTable);
180    } catch (IOException e) {
181      exception = e;
182    }
183    assertTrue(exception instanceof TableNotFoundException);
184
185    exception = null;
186    try {
187      this.admin.enableTable(nonexistentTable);
188    } catch (IOException e) {
189      exception = e;
190    }
191    assertTrue(exception instanceof TableNotFoundException);
192
193    exception = null;
194    try {
195      this.admin.modifyColumnFamily(nonexistentTable, nonexistentHcd);
196    } catch (IOException e) {
197      exception = e;
198    }
199    assertTrue(exception instanceof TableNotFoundException);
200
201    exception = null;
202    try {
203      HTableDescriptor htd = new HTableDescriptor(nonexistentTable);
204      htd.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
205      this.admin.modifyTable(htd.getTableName(), htd);
206    } catch (IOException e) {
207      exception = e;
208    }
209    assertTrue(exception instanceof TableNotFoundException);
210
211    // Now make it so at least the table exists and then do tests against a
212    // nonexistent column family -- see if we get right exceptions.
213    final TableName tableName = TableName.valueOf(name.getMethodName() + System.currentTimeMillis());
214    HTableDescriptor htd = new HTableDescriptor(tableName);
215    htd.addFamily(new HColumnDescriptor("cf"));
216    this.admin.createTable(htd);
217    try {
218      exception = null;
219      try {
220        this.admin.deleteColumnFamily(htd.getTableName(), nonexistentHcd.getName());
221      } catch (IOException e) {
222        exception = e;
223      }
224      assertTrue("found=" + exception.getClass().getName(),
225          exception instanceof InvalidFamilyOperationException);
226
227      exception = null;
228      try {
229        this.admin.modifyColumnFamily(htd.getTableName(), nonexistentHcd);
230      } catch (IOException e) {
231        exception = e;
232      }
233      assertTrue("found=" + exception.getClass().getName(),
234          exception instanceof InvalidFamilyOperationException);
235    } finally {
236      this.admin.disableTable(tableName);
237      this.admin.deleteTable(tableName);
238    }
239  }
240
241  @Test
242  public void testDisableAndEnableTable() throws IOException {
243    final byte [] row = Bytes.toBytes("row");
244    final byte [] qualifier = Bytes.toBytes("qualifier");
245    final byte [] value = Bytes.toBytes("value");
246    final TableName table = TableName.valueOf(name.getMethodName());
247    Table ht = TEST_UTIL.createTable(table, HConstants.CATALOG_FAMILY);
248    Put put = new Put(row);
249    put.addColumn(HConstants.CATALOG_FAMILY, qualifier, value);
250    ht.put(put);
251    Get get = new Get(row);
252    get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
253    ht.get(get);
254
255    this.admin.disableTable(ht.getName());
256    assertTrue("Table must be disabled.", TEST_UTIL.getHBaseCluster()
257        .getMaster().getTableStateManager().isTableState(
258            ht.getName(), TableState.State.DISABLED));
259    assertEquals(TableState.State.DISABLED, getStateFromMeta(table));
260
261    // Test that table is disabled
262    get = new Get(row);
263    get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
264    boolean ok = false;
265    try {
266      ht.get(get);
267    } catch (TableNotEnabledException e) {
268      ok = true;
269    }
270    ok = false;
271    // verify that scan encounters correct exception
272    Scan scan = new Scan();
273    try {
274      ResultScanner scanner = ht.getScanner(scan);
275      Result res = null;
276      do {
277        res = scanner.next();
278      } while (res != null);
279    } catch (TableNotEnabledException e) {
280      ok = true;
281    }
282    assertTrue(ok);
283    this.admin.enableTable(table);
284    assertTrue("Table must be enabled.", TEST_UTIL.getHBaseCluster()
285        .getMaster().getTableStateManager().isTableState(
286            ht.getName(), TableState.State.ENABLED));
287    assertEquals(TableState.State.ENABLED, getStateFromMeta(table));
288
289    // Test that table is enabled
290    try {
291      ht.get(get);
292    } catch (RetriesExhaustedException e) {
293      ok = false;
294    }
295    assertTrue(ok);
296    ht.close();
297  }
298
299  private TableState.State getStateFromMeta(TableName table) throws IOException {
300    TableState state =
301        MetaTableAccessor.getTableState(TEST_UTIL.getConnection(), table);
302    assertNotNull(state);
303    return state.getState();
304  }
305
306  @Test
307  public void testDisableAndEnableTables() throws IOException {
308    final byte [] row = Bytes.toBytes("row");
309    final byte [] qualifier = Bytes.toBytes("qualifier");
310    final byte [] value = Bytes.toBytes("value");
311    final TableName table1 = TableName.valueOf(name.getMethodName() + "1");
312    final TableName table2 = TableName.valueOf(name.getMethodName() + "2");
313    Table ht1 = TEST_UTIL.createTable(table1, HConstants.CATALOG_FAMILY);
314    Table ht2 = TEST_UTIL.createTable(table2, HConstants.CATALOG_FAMILY);
315    Put put = new Put(row);
316    put.addColumn(HConstants.CATALOG_FAMILY, qualifier, value);
317    ht1.put(put);
318    ht2.put(put);
319    Get get = new Get(row);
320    get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
321    ht1.get(get);
322    ht2.get(get);
323
324    this.admin.disableTables("testDisableAndEnableTable.*");
325
326    // Test that tables are disabled
327    get = new Get(row);
328    get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
329    boolean ok = false;
330    try {
331      ht1.get(get);
332      ht2.get(get);
333    } catch (org.apache.hadoop.hbase.DoNotRetryIOException e) {
334      ok = true;
335    }
336
337    assertEquals(TableState.State.DISABLED, getStateFromMeta(table1));
338    assertEquals(TableState.State.DISABLED, getStateFromMeta(table2));
339
340
341    assertTrue(ok);
342    this.admin.enableTables("testDisableAndEnableTable.*");
343
344    // Test that tables are enabled
345    try {
346      ht1.get(get);
347    } catch (IOException e) {
348      ok = false;
349    }
350    try {
351      ht2.get(get);
352    } catch (IOException e) {
353      ok = false;
354    }
355    assertTrue(ok);
356
357    ht1.close();
358    ht2.close();
359
360    assertEquals(TableState.State.ENABLED, getStateFromMeta(table1));
361    assertEquals(TableState.State.ENABLED, getStateFromMeta(table2));
362  }
363
364  @Test
365  public void testCreateTable() throws IOException {
366    HTableDescriptor [] tables = admin.listTables();
367    int numTables = tables.length;
368    final TableName tableName = TableName.valueOf(name.getMethodName());
369    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
370    tables = this.admin.listTables();
371    assertEquals(numTables + 1, tables.length);
372    assertTrue("Table must be enabled.",
373        TEST_UTIL.getHBaseCluster().getMaster().getTableStateManager()
374            .isTableState(tableName, TableState.State.ENABLED));
375    assertEquals(TableState.State.ENABLED, getStateFromMeta(tableName));
376  }
377
378  @Test
379  public void testTruncateTable() throws IOException {
380    testTruncateTable(TableName.valueOf(name.getMethodName()), false);
381  }
382
383  @Test
384  public void testTruncateTablePreservingSplits() throws IOException {
385    testTruncateTable(TableName.valueOf(name.getMethodName()), true);
386  }
387
388  private void testTruncateTable(final TableName tableName, boolean preserveSplits)
389      throws IOException {
390    byte[][] splitKeys = new byte[2][];
391    splitKeys[0] = Bytes.toBytes(4);
392    splitKeys[1] = Bytes.toBytes(8);
393
394    // Create & Fill the table
395    Table table = TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY, splitKeys);
396    try {
397      TEST_UTIL.loadNumericRows(table, HConstants.CATALOG_FAMILY, 0, 10);
398      assertEquals(10, TEST_UTIL.countRows(table));
399    } finally {
400      table.close();
401    }
402    assertEquals(3, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
403
404    // Truncate & Verify
405    this.admin.disableTable(tableName);
406    this.admin.truncateTable(tableName, preserveSplits);
407    table = TEST_UTIL.getConnection().getTable(tableName);
408    try {
409      assertEquals(0, TEST_UTIL.countRows(table));
410    } finally {
411      table.close();
412    }
413    if (preserveSplits) {
414      assertEquals(3, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
415    } else {
416      assertEquals(1, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
417    }
418  }
419
420  @Test
421  public void testGetTableDescriptor() throws IOException {
422    HColumnDescriptor fam1 = new HColumnDescriptor("fam1");
423    HColumnDescriptor fam2 = new HColumnDescriptor("fam2");
424    HColumnDescriptor fam3 = new HColumnDescriptor("fam3");
425    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name.getMethodName()));
426    htd.addFamily(fam1);
427    htd.addFamily(fam2);
428    htd.addFamily(fam3);
429    this.admin.createTable(htd);
430    Table table = TEST_UTIL.getConnection().getTable(htd.getTableName());
431    TableDescriptor confirmedHtd = table.getDescriptor();
432    assertEquals(0, TableDescriptor.COMPARATOR.compare(htd, confirmedHtd));
433    MetaTableAccessor.fullScanMetaAndPrint(TEST_UTIL.getConnection());
434    table.close();
435  }
436
437  @Test
438  public void testCompactionTimestamps() throws Exception {
439    HColumnDescriptor fam1 = new HColumnDescriptor("fam1");
440    final TableName tableName = TableName.valueOf(name.getMethodName());
441    HTableDescriptor htd = new HTableDescriptor(tableName);
442    htd.addFamily(fam1);
443    this.admin.createTable(htd);
444    Table table = TEST_UTIL.getConnection().getTable(htd.getTableName());
445    long ts = this.admin.getLastMajorCompactionTimestamp(tableName);
446    assertEquals(0, ts);
447    Put p = new Put(Bytes.toBytes("row1"));
448    p.addColumn(Bytes.toBytes("fam1"), Bytes.toBytes("fam1"), Bytes.toBytes("fam1"));
449    table.put(p);
450    ts = this.admin.getLastMajorCompactionTimestamp(tableName);
451    // no files written -> no data
452    assertEquals(0, ts);
453
454    this.admin.flush(tableName);
455    ts = this.admin.getLastMajorCompactionTimestamp(tableName);
456    // still 0, we flushed a file, but no major compaction happened
457    assertEquals(0, ts);
458
459    byte[] regionName;
460    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
461      regionName = l.getAllRegionLocations().get(0).getRegionInfo().getRegionName();
462    }
463    long ts1 = this.admin.getLastMajorCompactionTimestampForRegion(regionName);
464    assertEquals(ts, ts1);
465    p = new Put(Bytes.toBytes("row2"));
466    p.addColumn(Bytes.toBytes("fam1"), Bytes.toBytes("fam1"), Bytes.toBytes("fam1"));
467    table.put(p);
468    this.admin.flush(tableName);
469    ts = this.admin.getLastMajorCompactionTimestamp(tableName);
470    // make sure the region API returns the same value, as the old file is still around
471    assertEquals(ts1, ts);
472
473    TEST_UTIL.compact(tableName, true);
474    table.put(p);
475    // forces a wait for the compaction
476    this.admin.flush(tableName);
477    ts = this.admin.getLastMajorCompactionTimestamp(tableName);
478    // after a compaction our earliest timestamp will have progressed forward
479    assertTrue(ts > ts1);
480
481    // region api still the same
482    ts1 = this.admin.getLastMajorCompactionTimestampForRegion(regionName);
483    assertEquals(ts, ts1);
484    table.put(p);
485    this.admin.flush(tableName);
486    ts = this.admin.getLastMajorCompactionTimestamp(tableName);
487    assertEquals(ts, ts1);
488    table.close();
489  }
490
491  @Test
492  public void testHColumnValidName() {
493       boolean exceptionThrown;
494       try {
495         new HColumnDescriptor("\\test\\abc");
496       } catch(IllegalArgumentException iae) {
497           exceptionThrown = true;
498           assertTrue(exceptionThrown);
499       }
500   }
501
502  /**
503   * Verify schema change for read only table
504   */
505  @Test
506  public void testReadOnlyTableModify() throws IOException, InterruptedException {
507    final TableName tableName = TableName.valueOf(name.getMethodName());
508    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
509
510    // Make table read only
511    TableDescriptor htd = TableDescriptorBuilder.newBuilder(this.admin.getDescriptor(tableName))
512      .setReadOnly(true).build();
513    admin.modifyTable(htd);
514
515    // try to modify the read only table now
516    htd = TableDescriptorBuilder.newBuilder(this.admin.getDescriptor(tableName))
517      .setCompactionEnabled(false).build();
518    admin.modifyTable(htd);
519    // Delete the table
520    this.admin.disableTable(tableName);
521    this.admin.deleteTable(tableName);
522    assertFalse(this.admin.tableExists(tableName));
523  }
524
525  /**
526   * Verify schema modification takes.
527   */
528  @Test
529  public void testOnlineChangeTableSchema() throws IOException, InterruptedException {
530    final TableName tableName = TableName.valueOf(name.getMethodName());
531    HTableDescriptor [] tables = admin.listTables();
532    int numTables = tables.length;
533    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
534    tables = this.admin.listTables();
535    assertEquals(numTables + 1, tables.length);
536
537    // FIRST, do htabledescriptor changes.
538    HTableDescriptor htd = this.admin.getTableDescriptor(tableName);
539    // Make a copy and assert copy is good.
540    HTableDescriptor copy = new HTableDescriptor(htd);
541    assertTrue(htd.equals(copy));
542    // Now amend the copy. Introduce differences.
543    long newFlushSize = htd.getMemStoreFlushSize() / 2;
544    if (newFlushSize <=0) {
545      newFlushSize = HTableDescriptor.DEFAULT_MEMSTORE_FLUSH_SIZE / 2;
546    }
547    copy.setMemStoreFlushSize(newFlushSize);
548    final String key = "anyoldkey";
549    assertTrue(htd.getValue(key) == null);
550    copy.setValue(key, key);
551    boolean expectedException = false;
552    try {
553      admin.modifyTable(tableName, copy);
554    } catch (TableNotDisabledException re) {
555      expectedException = true;
556    }
557    assertFalse(expectedException);
558    HTableDescriptor modifiedHtd = new HTableDescriptor(this.admin.getTableDescriptor(tableName));
559    assertFalse(htd.equals(modifiedHtd));
560    assertTrue(copy.equals(modifiedHtd));
561    assertEquals(newFlushSize, modifiedHtd.getMemStoreFlushSize());
562    assertEquals(key, modifiedHtd.getValue(key));
563
564    // Now work on column family changes.
565    int countOfFamilies = modifiedHtd.getFamilies().size();
566    assertTrue(countOfFamilies > 0);
567    HColumnDescriptor hcd = modifiedHtd.getFamilies().iterator().next();
568    int maxversions = hcd.getMaxVersions();
569    final int newMaxVersions = maxversions + 1;
570    hcd.setMaxVersions(newMaxVersions);
571    final byte [] hcdName = hcd.getName();
572    expectedException = false;
573    try {
574      this.admin.modifyColumnFamily(tableName, hcd);
575    } catch (TableNotDisabledException re) {
576      expectedException = true;
577    }
578    assertFalse(expectedException);
579    modifiedHtd = this.admin.getTableDescriptor(tableName);
580    HColumnDescriptor modifiedHcd = modifiedHtd.getFamily(hcdName);
581    assertEquals(newMaxVersions, modifiedHcd.getMaxVersions());
582
583    // Try adding a column
584    assertFalse(this.admin.isTableDisabled(tableName));
585    final String xtracolName = "xtracol";
586    HColumnDescriptor xtracol = new HColumnDescriptor(xtracolName);
587    xtracol.setValue(xtracolName, xtracolName);
588    expectedException = false;
589    try {
590      this.admin.addColumnFamily(tableName, xtracol);
591    } catch (TableNotDisabledException re) {
592      expectedException = true;
593    }
594    // Add column should work even if the table is enabled
595    assertFalse(expectedException);
596    modifiedHtd = this.admin.getTableDescriptor(tableName);
597    hcd = modifiedHtd.getFamily(xtracol.getName());
598    assertTrue(hcd != null);
599    assertTrue(hcd.getValue(xtracolName).equals(xtracolName));
600
601    // Delete the just-added column.
602    this.admin.deleteColumnFamily(tableName, xtracol.getName());
603    modifiedHtd = this.admin.getTableDescriptor(tableName);
604    hcd = modifiedHtd.getFamily(xtracol.getName());
605    assertTrue(hcd == null);
606
607    // Delete the table
608    this.admin.disableTable(tableName);
609    this.admin.deleteTable(tableName);
610    this.admin.listTables();
611    assertFalse(this.admin.tableExists(tableName));
612  }
613
614  protected void verifyRoundRobinDistribution(ClusterConnection c, RegionLocator regionLocator, int
615      expectedRegions) throws IOException {
616    int numRS = c.getCurrentNrHRS();
617    List<HRegionLocation> regions = regionLocator.getAllRegionLocations();
618    Map<ServerName, List<RegionInfo>> server2Regions = new HashMap<>();
619    for (HRegionLocation loc : regions) {
620      ServerName server = loc.getServerName();
621      List<RegionInfo> regs = server2Regions.get(server);
622      if (regs == null) {
623        regs = new ArrayList<>();
624        server2Regions.put(server, regs);
625      }
626      regs.add(loc.getRegionInfo());
627    }
628    boolean tablesOnMaster = LoadBalancer.isTablesOnMaster(TEST_UTIL.getConfiguration());
629    if (tablesOnMaster) {
630      // Ignore the master region server,
631      // which contains less regions by intention.
632      numRS--;
633    }
634    float average = (float) expectedRegions/numRS;
635    int min = (int)Math.floor(average);
636    int max = (int)Math.ceil(average);
637    for (List<RegionInfo> regionList : server2Regions.values()) {
638      assertTrue("numRS=" + numRS + ", min=" + min + ", max=" + max +
639        ", size=" + regionList.size() + ", tablesOnMaster=" + tablesOnMaster,
640      regionList.size() == min || regionList.size() == max);
641    }
642  }
643
644  @Test
645  public void testCreateTableNumberOfRegions() throws IOException, InterruptedException {
646    final TableName tableName = TableName.valueOf(name.getMethodName());
647    HTableDescriptor desc = new HTableDescriptor(tableName);
648    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
649    admin.createTable(desc);
650    List<HRegionLocation> regions;
651    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
652      regions = l.getAllRegionLocations();
653      assertEquals("Table should have only 1 region", 1, regions.size());
654    }
655
656    TableName TABLE_2 = TableName.valueOf(tableName.getNameAsString() + "_2");
657    desc = new HTableDescriptor(TABLE_2);
658    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
659    admin.createTable(desc, new byte[][]{new byte[]{42}});
660    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(TABLE_2)) {
661      regions = l.getAllRegionLocations();
662      assertEquals("Table should have only 2 region", 2, regions.size());
663    }
664
665    TableName TABLE_3 = TableName.valueOf(tableName.getNameAsString() + "_3");
666    desc = new HTableDescriptor(TABLE_3);
667    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
668    admin.createTable(desc, "a".getBytes(), "z".getBytes(), 3);
669    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(TABLE_3)) {
670      regions = l.getAllRegionLocations();
671      assertEquals("Table should have only 3 region", 3, regions.size());
672    }
673
674    TableName TABLE_4 = TableName.valueOf(tableName.getNameAsString() + "_4");
675    desc = new HTableDescriptor(TABLE_4);
676    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
677    try {
678      admin.createTable(desc, "a".getBytes(), "z".getBytes(), 2);
679      fail("Should not be able to create a table with only 2 regions using this API.");
680    } catch (IllegalArgumentException eae) {
681    // Expected
682    }
683
684    TableName TABLE_5 = TableName.valueOf(tableName.getNameAsString() + "_5");
685    desc = new HTableDescriptor(TABLE_5);
686    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
687    admin.createTable(desc, new byte[] { 1 }, new byte[] { 127 }, 16);
688    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(TABLE_5)) {
689      regions = l.getAllRegionLocations();
690      assertEquals("Table should have 16 region", 16, regions.size());
691    }
692  }
693
694  @Test
695  public void testCreateTableWithRegions() throws IOException, InterruptedException {
696    final TableName tableName = TableName.valueOf(name.getMethodName());
697
698    byte [][] splitKeys = {
699        new byte [] { 1, 1, 1 },
700        new byte [] { 2, 2, 2 },
701        new byte [] { 3, 3, 3 },
702        new byte [] { 4, 4, 4 },
703        new byte [] { 5, 5, 5 },
704        new byte [] { 6, 6, 6 },
705        new byte [] { 7, 7, 7 },
706        new byte [] { 8, 8, 8 },
707        new byte [] { 9, 9, 9 },
708    };
709    int expectedRegions = splitKeys.length + 1;
710
711    HTableDescriptor desc = new HTableDescriptor(tableName);
712    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
713    admin.createTable(desc, splitKeys);
714
715    boolean tableAvailable = admin.isTableAvailable(tableName, splitKeys);
716    assertTrue("Table should be created with splitKyes + 1 rows in META", tableAvailable);
717
718    List<HRegionLocation> regions;
719    Iterator<HRegionLocation> hris;
720    RegionInfo hri;
721    ClusterConnection conn = (ClusterConnection) TEST_UTIL.getConnection();
722    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
723      regions = l.getAllRegionLocations();
724
725      assertEquals("Tried to create " + expectedRegions + " regions " +
726              "but only found " + regions.size(), expectedRegions, regions.size());
727      System.err.println("Found " + regions.size() + " regions");
728
729      hris = regions.iterator();
730      hri = hris.next().getRegionInfo();
731      assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
732      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[0]));
733      hri = hris.next().getRegionInfo();
734      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[0]));
735      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[1]));
736      hri = hris.next().getRegionInfo();
737      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[1]));
738      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[2]));
739      hri = hris.next().getRegionInfo();
740      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[2]));
741      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[3]));
742      hri = hris.next().getRegionInfo();
743      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[3]));
744      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[4]));
745      hri = hris.next().getRegionInfo();
746      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[4]));
747      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[5]));
748      hri = hris.next().getRegionInfo();
749      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[5]));
750      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[6]));
751      hri = hris.next().getRegionInfo();
752      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[6]));
753      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[7]));
754      hri = hris.next().getRegionInfo();
755      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[7]));
756      assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[8]));
757      hri = hris.next().getRegionInfo();
758      assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[8]));
759      assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
760
761      verifyRoundRobinDistribution(conn, l, expectedRegions);
762    }
763
764
765
766    // Now test using start/end with a number of regions
767
768    // Use 80 bit numbers to make sure we aren't limited
769    byte [] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
770    byte [] endKey =   { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
771
772    // Splitting into 10 regions, we expect (null,1) ... (9, null)
773    // with (1,2) (2,3) (3,4) (4,5) (5,6) (6,7) (7,8) (8,9) in the middle
774
775    expectedRegions = 10;
776
777    TableName TABLE_2 = TableName.valueOf(tableName.getNameAsString() + "_2");
778
779    desc = new HTableDescriptor(TABLE_2);
780    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
781    admin = TEST_UTIL.getAdmin();
782    admin.createTable(desc, startKey, endKey, expectedRegions);
783
784    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(TABLE_2)) {
785      regions = l.getAllRegionLocations();
786      assertEquals("Tried to create " + expectedRegions + " regions " +
787          "but only found " + regions.size(), expectedRegions, regions.size());
788      System.err.println("Found " + regions.size() + " regions");
789
790      hris = regions.iterator();
791      hri = hris.next().getRegionInfo();
792      assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
793      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }));
794      hri = hris.next().getRegionInfo();
795      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }));
796      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }));
797      hri = hris.next().getRegionInfo();
798      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }));
799      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }));
800      hri = hris.next().getRegionInfo();
801      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }));
802      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }));
803      hri = hris.next().getRegionInfo();
804      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }));
805      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }));
806      hri = hris.next().getRegionInfo();
807      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }));
808      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }));
809      hri = hris.next().getRegionInfo();
810      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }));
811      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }));
812      hri = hris.next().getRegionInfo();
813      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }));
814      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }));
815      hri = hris.next().getRegionInfo();
816      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }));
817      assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }));
818      hri = hris.next().getRegionInfo();
819      assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }));
820      assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
821
822      verifyRoundRobinDistribution(conn, l, expectedRegions);
823    }
824
825    // Try once more with something that divides into something infinite
826
827    startKey = new byte [] { 0, 0, 0, 0, 0, 0 };
828    endKey = new byte [] { 1, 0, 0, 0, 0, 0 };
829
830    expectedRegions = 5;
831
832    TableName TABLE_3 = TableName.valueOf(tableName.getNameAsString() + "_3");
833
834    desc = new HTableDescriptor(TABLE_3);
835    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
836    admin = TEST_UTIL.getAdmin();
837    admin.createTable(desc, startKey, endKey, expectedRegions);
838
839
840    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(TABLE_3)) {
841      regions = l.getAllRegionLocations();
842      assertEquals("Tried to create " + expectedRegions + " regions " +
843          "but only found " + regions.size(), expectedRegions, regions.size());
844      System.err.println("Found " + regions.size() + " regions");
845
846      verifyRoundRobinDistribution(conn, l, expectedRegions);
847    }
848
849
850    // Try an invalid case where there are duplicate split keys
851    splitKeys = new byte [][] {
852        new byte [] { 1, 1, 1 },
853        new byte [] { 2, 2, 2 },
854        new byte [] { 3, 3, 3 },
855        new byte [] { 2, 2, 2 }
856    };
857
858    TableName TABLE_4 = TableName.valueOf(tableName.getNameAsString() + "_4");
859    desc = new HTableDescriptor(TABLE_4);
860    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
861    try {
862      admin.createTable(desc, splitKeys);
863      assertTrue("Should not be able to create this table because of " +
864          "duplicate split keys", false);
865    } catch(IllegalArgumentException iae) {
866      // Expected
867    }
868  }
869
870  @Test
871  public void testTableAvailableWithRandomSplitKeys() throws Exception {
872    final TableName tableName = TableName.valueOf(name.getMethodName());
873    HTableDescriptor desc = new HTableDescriptor(tableName);
874    desc.addFamily(new HColumnDescriptor("col"));
875    byte[][] splitKeys = new byte[1][];
876    splitKeys = new byte [][] {
877        new byte [] { 1, 1, 1 },
878        new byte [] { 2, 2, 2 }
879    };
880    admin.createTable(desc);
881    boolean tableAvailable = admin.isTableAvailable(tableName, splitKeys);
882    assertFalse("Table should be created with 1 row in META", tableAvailable);
883  }
884
885  @Test
886  public void testCreateTableWithOnlyEmptyStartRow() throws IOException {
887    final byte[] tableName = Bytes.toBytes(name.getMethodName());
888    byte[][] splitKeys = new byte[1][];
889    splitKeys[0] = HConstants.EMPTY_BYTE_ARRAY;
890    HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
891    desc.addFamily(new HColumnDescriptor("col"));
892    try {
893      admin.createTable(desc, splitKeys);
894      fail("Test case should fail as empty split key is passed.");
895    } catch (IllegalArgumentException e) {
896    }
897  }
898
899  @Test
900  public void testCreateTableWithEmptyRowInTheSplitKeys() throws IOException{
901    final byte[] tableName = Bytes.toBytes(name.getMethodName());
902    byte[][] splitKeys = new byte[3][];
903    splitKeys[0] = "region1".getBytes();
904    splitKeys[1] = HConstants.EMPTY_BYTE_ARRAY;
905    splitKeys[2] = "region2".getBytes();
906    HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
907    desc.addFamily(new HColumnDescriptor("col"));
908    try {
909      admin.createTable(desc, splitKeys);
910      fail("Test case should fail as empty split key is passed.");
911    } catch (IllegalArgumentException e) {
912      LOG.info("Expected ", e);
913    }
914  }
915
916  @Test
917  public void testTableExist() throws IOException {
918    final TableName table = TableName.valueOf(name.getMethodName());
919    boolean exist;
920    exist = this.admin.tableExists(table);
921    assertEquals(false, exist);
922    TEST_UTIL.createTable(table, HConstants.CATALOG_FAMILY);
923    exist = this.admin.tableExists(table);
924    assertEquals(true, exist);
925  }
926
927  /**
928   * Tests forcing split from client and having scanners successfully ride over split.
929   * @throws Exception
930   * @throws IOException
931   */
932  @Test
933  public void testForceSplit() throws Exception {
934    byte[][] familyNames = new byte[][] { Bytes.toBytes("cf") };
935    int[] rowCounts = new int[] { 6000 };
936    int numVersions = HColumnDescriptor.DEFAULT_VERSIONS;
937    int blockSize = 256;
938    splitTest(null, familyNames, rowCounts, numVersions, blockSize, true);
939
940    byte[] splitKey = Bytes.toBytes(3500);
941    splitTest(splitKey, familyNames, rowCounts, numVersions, blockSize, true);
942    // test regionSplitSync
943    splitTest(splitKey, familyNames, rowCounts, numVersions, blockSize, false);
944  }
945
946  /**
947   * Test retain assignment on enableTable.
948   *
949   * @throws IOException
950   */
951  @Test
952  public void testEnableTableRetainAssignment() throws IOException {
953    final TableName tableName = TableName.valueOf(name.getMethodName());
954    byte[][] splitKeys = { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 },
955        new byte[] { 3, 3, 3 }, new byte[] { 4, 4, 4 }, new byte[] { 5, 5, 5 },
956        new byte[] { 6, 6, 6 }, new byte[] { 7, 7, 7 }, new byte[] { 8, 8, 8 },
957        new byte[] { 9, 9, 9 } };
958    int expectedRegions = splitKeys.length + 1;
959    HTableDescriptor desc = new HTableDescriptor(tableName);
960    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
961    admin.createTable(desc, splitKeys);
962
963    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
964      List<HRegionLocation> regions = l.getAllRegionLocations();
965
966      assertEquals(
967          "Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(),
968          expectedRegions, regions.size());
969      // Disable table.
970      admin.disableTable(tableName);
971      // Enable table, use retain assignment to assign regions.
972      admin.enableTable(tableName);
973      List<HRegionLocation> regions2 = l.getAllRegionLocations();
974
975      // Check the assignment.
976      assertEquals(regions.size(), regions2.size());
977      assertTrue(regions2.containsAll(regions));
978    }
979  }
980
981  /**
982   * Multi-family scenario. Tests forcing split from client and
983   * having scanners successfully ride over split.
984   * @throws Exception
985   * @throws IOException
986   */
987  @Test
988  public void testForceSplitMultiFamily() throws Exception {
989    int numVersions = HColumnDescriptor.DEFAULT_VERSIONS;
990
991    // use small HFile block size so that we can have lots of blocks in HFile
992    // Otherwise, if there is only one block,
993    // HFileBlockIndex.midKey()'s value == startKey
994    int blockSize = 256;
995    byte[][] familyNames = new byte[][] { Bytes.toBytes("cf1"),
996      Bytes.toBytes("cf2") };
997
998    // one of the column families isn't splittable
999    int[] rowCounts = new int[] { 6000, 1 };
1000    splitTest(null, familyNames, rowCounts, numVersions, blockSize, true);
1001
1002    rowCounts = new int[] { 1, 6000 };
1003    splitTest(null, familyNames, rowCounts, numVersions, blockSize, true);
1004
1005    // one column family has much smaller data than the other
1006    // the split key should be based on the largest column family
1007    rowCounts = new int[] { 6000, 300 };
1008    splitTest(null, familyNames, rowCounts, numVersions, blockSize, true);
1009
1010    rowCounts = new int[] { 300, 6000 };
1011    splitTest(null, familyNames, rowCounts, numVersions, blockSize, true);
1012
1013  }
1014
1015  void splitTest(byte[] splitPoint, byte[][] familyNames, int[] rowCounts,
1016    int numVersions, int blockSize, boolean async) throws Exception {
1017    TableName tableName = TableName.valueOf("testForceSplit");
1018    StringBuilder sb = new StringBuilder();
1019    // Add tail to String so can see better in logs where a test is running.
1020    for (int i = 0; i < rowCounts.length; i++) {
1021      sb.append("_").append(Integer.toString(rowCounts[i]));
1022    }
1023    assertFalse(admin.tableExists(tableName));
1024    try (final Table table = TEST_UTIL.createTable(tableName, familyNames,
1025      numVersions, blockSize);
1026      final RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
1027
1028      int rowCount = 0;
1029      byte[] q = new byte[0];
1030
1031      // insert rows into column families. The number of rows that have values
1032      // in a specific column family is decided by rowCounts[familyIndex]
1033      for (int index = 0; index < familyNames.length; index++) {
1034        ArrayList<Put> puts = new ArrayList<>(rowCounts[index]);
1035        for (int i = 0; i < rowCounts[index]; i++) {
1036          byte[] k = Bytes.toBytes(i);
1037          Put put = new Put(k);
1038          put.addColumn(familyNames[index], q, k);
1039          puts.add(put);
1040        }
1041        table.put(puts);
1042
1043        if (rowCount < rowCounts[index]) {
1044          rowCount = rowCounts[index];
1045        }
1046      }
1047
1048      // get the initial layout (should just be one region)
1049      List<HRegionLocation> m = locator.getAllRegionLocations();
1050      LOG.info("Initial regions (" + m.size() + "): " + m);
1051      assertTrue(m.size() == 1);
1052
1053      // Verify row count
1054      Scan scan = new Scan();
1055      ResultScanner scanner = table.getScanner(scan);
1056      int rows = 0;
1057      for (@SuppressWarnings("unused") Result result : scanner) {
1058        rows++;
1059      }
1060      scanner.close();
1061      assertEquals(rowCount, rows);
1062
1063      // Have an outstanding scan going on to make sure we can scan over splits.
1064      scan = new Scan();
1065      scanner = table.getScanner(scan);
1066      // Scan first row so we are into first region before split happens.
1067      scanner.next();
1068
1069      // Split the table
1070      if (async) {
1071        this.admin.split(tableName, splitPoint);
1072        final AtomicInteger count = new AtomicInteger(0);
1073        Thread t = new Thread("CheckForSplit") {
1074          @Override public void run() {
1075            for (int i = 0; i < 45; i++) {
1076              try {
1077                sleep(1000);
1078              } catch (InterruptedException e) {
1079                continue;
1080              }
1081              // check again
1082              List<HRegionLocation> regions = null;
1083              try {
1084                regions = locator.getAllRegionLocations();
1085              } catch (IOException e) {
1086                e.printStackTrace();
1087              }
1088              if (regions == null) continue;
1089              count.set(regions.size());
1090              if (count.get() >= 2) {
1091                LOG.info("Found: " + regions);
1092                break;
1093              }
1094              LOG.debug("Cycle waiting on split");
1095            }
1096            LOG.debug("CheckForSplit thread exited, current region count: " + count.get());
1097          }
1098        };
1099        t.setPriority(Thread.NORM_PRIORITY - 2);
1100        t.start();
1101        t.join();
1102      } else {
1103        // Sync split region, no need to create a thread to check
1104        ((HBaseAdmin)admin).splitRegionSync(m.get(0).getRegionInfo().getRegionName(), splitPoint);
1105      }
1106
1107      // Verify row count
1108      rows = 1; // We counted one row above.
1109      for (@SuppressWarnings("unused") Result result : scanner) {
1110        rows++;
1111        if (rows > rowCount) {
1112          scanner.close();
1113          assertTrue("Scanned more than expected (" + rowCount + ")", false);
1114        }
1115      }
1116      scanner.close();
1117      assertEquals(rowCount, rows);
1118
1119      List<HRegionLocation> regions = null;
1120      try {
1121        regions = locator.getAllRegionLocations();
1122      } catch (IOException e) {
1123        e.printStackTrace();
1124      }
1125      assertEquals(2, regions.size());
1126      if (splitPoint != null) {
1127        // make sure the split point matches our explicit configuration
1128        assertEquals(Bytes.toString(splitPoint),
1129            Bytes.toString(regions.get(0).getRegionInfo().getEndKey()));
1130        assertEquals(Bytes.toString(splitPoint),
1131            Bytes.toString(regions.get(1).getRegionInfo().getStartKey()));
1132        LOG.debug("Properly split on " + Bytes.toString(splitPoint));
1133      } else {
1134        if (familyNames.length > 1) {
1135          int splitKey = Bytes.toInt(regions.get(0).getRegionInfo().getEndKey());
1136          // check if splitKey is based on the largest column family
1137          // in terms of it store size
1138          int deltaForLargestFamily = Math.abs(rowCount / 2 - splitKey);
1139          LOG.debug("SplitKey=" + splitKey + "&deltaForLargestFamily=" + deltaForLargestFamily +
1140              ", r=" + regions.get(0).getRegionInfo());
1141          for (int index = 0; index < familyNames.length; index++) {
1142            int delta = Math.abs(rowCounts[index] / 2 - splitKey);
1143            if (delta < deltaForLargestFamily) {
1144              assertTrue("Delta " + delta + " for family " + index + " should be at least "
1145                      + "deltaForLargestFamily " + deltaForLargestFamily, false);
1146            }
1147          }
1148        }
1149      }
1150      TEST_UTIL.deleteTable(tableName);
1151    }
1152  }
1153
1154  @Test
1155  public void testSplitAndMergeWithReplicaTable() throws Exception {
1156    // The test tries to directly split replica regions and directly merge replica regions. These
1157    // are not allowed. The test validates that. Then the test does a valid split/merge of allowed
1158    // regions.
1159    // Set up a table with 3 regions and replication set to 3
1160    final TableName tableName = TableName.valueOf(name.getMethodName());
1161    HTableDescriptor desc = new HTableDescriptor(tableName);
1162    desc.setRegionReplication(3);
1163    byte[] cf = "f".getBytes();
1164    HColumnDescriptor hcd = new HColumnDescriptor(cf);
1165    desc.addFamily(hcd);
1166    byte[][] splitRows = new byte[2][];
1167    splitRows[0] = new byte[]{(byte)'4'};
1168    splitRows[1] = new byte[]{(byte)'7'};
1169    TEST_UTIL.getAdmin().createTable(desc, splitRows);
1170    List<HRegion> oldRegions;
1171    do {
1172      oldRegions = TEST_UTIL.getHBaseCluster().getRegions(tableName);
1173      Thread.sleep(10);
1174    } while (oldRegions.size() != 9); //3 regions * 3 replicas
1175    // write some data to the table
1176    Table ht = TEST_UTIL.getConnection().getTable(tableName);
1177    List<Put> puts = new ArrayList<>();
1178    byte[] qualifier = "c".getBytes();
1179    Put put = new Put(new byte[]{(byte)'1'});
1180    put.addColumn(cf, qualifier, "100".getBytes());
1181    puts.add(put);
1182    put = new Put(new byte[]{(byte)'6'});
1183    put.addColumn(cf, qualifier, "100".getBytes());
1184    puts.add(put);
1185    put = new Put(new byte[]{(byte)'8'});
1186    put.addColumn(cf, qualifier, "100".getBytes());
1187    puts.add(put);
1188    ht.put(puts);
1189    ht.close();
1190    List<Pair<RegionInfo, ServerName>> regions =
1191        MetaTableAccessor.getTableRegionsAndLocations(TEST_UTIL.getConnection(), tableName);
1192    boolean gotException = false;
1193    // the element at index 1 would be a replica (since the metareader gives us ordered
1194    // regions). Try splitting that region via the split API . Should fail
1195    try {
1196      TEST_UTIL.getAdmin().splitRegion(regions.get(1).getFirst().getRegionName());
1197    } catch (IllegalArgumentException ex) {
1198      gotException = true;
1199    }
1200    assertTrue(gotException);
1201    gotException = false;
1202    // the element at index 1 would be a replica (since the metareader gives us ordered
1203    // regions). Try splitting that region via a different split API (the difference is
1204    // this API goes direct to the regionserver skipping any checks in the admin). Should fail
1205    try {
1206      TEST_UTIL.getHBaseAdmin().splitRegionAsync(regions.get(1).getFirst(),
1207          new byte[]{(byte)'1'});
1208    } catch (IOException ex) {
1209      gotException = true;
1210    }
1211    assertTrue(gotException);
1212
1213    gotException = false;
1214    //testing Sync split operation
1215    try {
1216      TEST_UTIL.getHBaseAdmin().splitRegionSync(regions.get(1).getFirst().getRegionName(),
1217          new byte[]{(byte)'1'});
1218    } catch (IllegalArgumentException ex) {
1219      gotException = true;
1220    }
1221    assertTrue(gotException);
1222
1223    gotException = false;
1224    // Try merging a replica with another. Should fail.
1225    try {
1226      TEST_UTIL.getHBaseAdmin().mergeRegionsSync(
1227        regions.get(1).getFirst().getEncodedNameAsBytes(),
1228        regions.get(2).getFirst().getEncodedNameAsBytes(),
1229        true);
1230    } catch (IllegalArgumentException m) {
1231      gotException = true;
1232    }
1233    assertTrue(gotException);
1234    // Try going to the master directly (that will skip the check in admin)
1235    try {
1236      byte[][] nameofRegionsToMerge = new byte[2][];
1237      nameofRegionsToMerge[0] =  regions.get(1).getFirst().getEncodedNameAsBytes();
1238      nameofRegionsToMerge[1] = regions.get(2).getFirst().getEncodedNameAsBytes();
1239      MergeTableRegionsRequest request = RequestConverter
1240          .buildMergeTableRegionsRequest(
1241            nameofRegionsToMerge,
1242            true,
1243            HConstants.NO_NONCE,
1244            HConstants.NO_NONCE);
1245      ((ClusterConnection) TEST_UTIL.getAdmin().getConnection()).getMaster()
1246        .mergeTableRegions(null, request);
1247    } catch (org.apache.hbase.thirdparty.com.google.protobuf.ServiceException m) {
1248      Throwable t = m.getCause();
1249      do {
1250        if (t instanceof MergeRegionException) {
1251          gotException = true;
1252          break;
1253        }
1254        t = t.getCause();
1255      } while (t != null);
1256    }
1257    assertTrue(gotException);
1258  }
1259
1260  @Test (expected=IllegalArgumentException.class)
1261  public void testInvalidHColumnDescriptor() throws IOException {
1262     new HColumnDescriptor("/cfamily/name");
1263  }
1264
1265  @Test
1266  public void testEnableDisableAddColumnDeleteColumn() throws Exception {
1267    final TableName tableName = TableName.valueOf(name.getMethodName());
1268    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
1269    while (!this.admin.isTableEnabled(TableName.valueOf(name.getMethodName()))) {
1270      Thread.sleep(10);
1271    }
1272    this.admin.disableTable(tableName);
1273    try {
1274      TEST_UTIL.getConnection().getTable(tableName);
1275    } catch (org.apache.hadoop.hbase.DoNotRetryIOException e) {
1276      //expected
1277    }
1278
1279    this.admin.addColumnFamily(tableName, new HColumnDescriptor("col2"));
1280    this.admin.enableTable(tableName);
1281    try {
1282      this.admin.deleteColumnFamily(tableName, Bytes.toBytes("col2"));
1283    } catch (TableNotDisabledException e) {
1284      LOG.info(e.toString(), e);
1285    }
1286    this.admin.disableTable(tableName);
1287    this.admin.deleteTable(tableName);
1288  }
1289
1290  @Test
1291  public void testDeleteLastColumnFamily() throws Exception {
1292    final TableName tableName = TableName.valueOf(name.getMethodName());
1293    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
1294    while (!this.admin.isTableEnabled(TableName.valueOf(name.getMethodName()))) {
1295      Thread.sleep(10);
1296    }
1297
1298    // test for enabled table
1299    try {
1300      this.admin.deleteColumnFamily(tableName, HConstants.CATALOG_FAMILY);
1301      fail("Should have failed to delete the only column family of a table");
1302    } catch (InvalidFamilyOperationException ex) {
1303      // expected
1304    }
1305
1306    // test for disabled table
1307    this.admin.disableTable(tableName);
1308
1309    try {
1310      this.admin.deleteColumnFamily(tableName, HConstants.CATALOG_FAMILY);
1311      fail("Should have failed to delete the only column family of a table");
1312    } catch (InvalidFamilyOperationException ex) {
1313      // expected
1314    }
1315
1316    this.admin.deleteTable(tableName);
1317  }
1318
1319  /*
1320   * Test DFS replication for column families, where one CF has default replication(3) and the other
1321   * is set to 1.
1322   */
1323  @Test
1324  public void testHFileReplication() throws Exception {
1325    final TableName tableName = TableName.valueOf(this.name.getMethodName());
1326    String fn1 = "rep1";
1327    HColumnDescriptor hcd1 = new HColumnDescriptor(fn1);
1328    hcd1.setDFSReplication((short) 1);
1329    String fn = "defaultRep";
1330    HColumnDescriptor hcd = new HColumnDescriptor(fn);
1331    HTableDescriptor htd = new HTableDescriptor(tableName);
1332    htd.addFamily(hcd);
1333    htd.addFamily(hcd1);
1334    Table table = TEST_UTIL.createTable(htd, null);
1335    TEST_UTIL.waitTableAvailable(tableName);
1336    Put p = new Put(Bytes.toBytes("defaultRep_rk"));
1337    byte[] q1 = Bytes.toBytes("q1");
1338    byte[] v1 = Bytes.toBytes("v1");
1339    p.addColumn(Bytes.toBytes(fn), q1, v1);
1340    List<Put> puts = new ArrayList<>(2);
1341    puts.add(p);
1342    p = new Put(Bytes.toBytes("rep1_rk"));
1343    p.addColumn(Bytes.toBytes(fn1), q1, v1);
1344    puts.add(p);
1345    try {
1346      table.put(puts);
1347      admin.flush(tableName);
1348
1349      List<HRegion> regions = TEST_UTIL.getMiniHBaseCluster().getRegions(tableName);
1350      for (HRegion r : regions) {
1351        HStore store = r.getStore(Bytes.toBytes(fn));
1352        for (HStoreFile sf : store.getStorefiles()) {
1353          assertTrue(sf.toString().contains(fn));
1354          assertTrue("Column family " + fn + " should have 3 copies",
1355            FSUtils.getDefaultReplication(TEST_UTIL.getTestFileSystem(), sf.getPath()) == (sf
1356                .getFileInfo().getFileStatus().getReplication()));
1357        }
1358
1359        store = r.getStore(Bytes.toBytes(fn1));
1360        for (HStoreFile sf : store.getStorefiles()) {
1361          assertTrue(sf.toString().contains(fn1));
1362          assertTrue("Column family " + fn1 + " should have only 1 copy", 1 == sf.getFileInfo()
1363              .getFileStatus().getReplication());
1364        }
1365      }
1366    } finally {
1367      if (admin.isTableEnabled(tableName)) {
1368        this.admin.disableTable(tableName);
1369        this.admin.deleteTable(tableName);
1370      }
1371    }
1372  }
1373
1374  @Test
1375  public void testMergeRegions() throws Exception {
1376    final TableName tableName = TableName.valueOf(name.getMethodName());
1377    HColumnDescriptor cd = new HColumnDescriptor("d");
1378    HTableDescriptor td = new HTableDescriptor(tableName);
1379    td.addFamily(cd);
1380    byte[][] splitRows = new byte[2][];
1381    splitRows[0] = new byte[]{(byte)'3'};
1382    splitRows[1] = new byte[]{(byte)'6'};
1383    try {
1384      TEST_UTIL.createTable(td, splitRows);
1385      TEST_UTIL.waitTableAvailable(tableName);
1386
1387      List<RegionInfo> tableRegions;
1388      RegionInfo regionA;
1389      RegionInfo regionB;
1390
1391      // merge with full name
1392      tableRegions = admin.getRegions(tableName);
1393      assertEquals(3, admin.getTableRegions(tableName).size());
1394      regionA = tableRegions.get(0);
1395      regionB = tableRegions.get(1);
1396      // TODO convert this to version that is synchronous (See HBASE-16668)
1397      admin.mergeRegionsAsync(regionA.getRegionName(), regionB.getRegionName(), false)
1398          .get(60, TimeUnit.SECONDS);
1399
1400      assertEquals(2, admin.getTableRegions(tableName).size());
1401
1402      // merge with encoded name
1403      tableRegions = admin.getRegions(tableName);
1404      regionA = tableRegions.get(0);
1405      regionB = tableRegions.get(1);
1406      // TODO convert this to version that is synchronous (See HBASE-16668)
1407      admin.mergeRegionsAsync(
1408        regionA.getEncodedNameAsBytes(), regionB.getEncodedNameAsBytes(), false)
1409          .get(60, TimeUnit.SECONDS);
1410
1411      assertEquals(1, admin.getTableRegions(tableName).size());
1412    } finally {
1413      this.admin.disableTable(tableName);
1414      this.admin.deleteTable(tableName);
1415    }
1416  }
1417
1418  @Test
1419  public void testSplitShouldNotHappenIfSplitIsDisabledForTable()
1420      throws Exception {
1421    final TableName tableName = TableName.valueOf(name.getMethodName());
1422    HTableDescriptor htd = new HTableDescriptor(tableName);
1423    htd.addFamily(new HColumnDescriptor("f"));
1424    htd.setRegionSplitPolicyClassName(DisabledRegionSplitPolicy.class.getName());
1425    Table table = TEST_UTIL.createTable(htd, null);
1426    for(int i = 0; i < 10; i++) {
1427      Put p = new Put(Bytes.toBytes("row"+i));
1428      byte[] q1 = Bytes.toBytes("q1");
1429      byte[] v1 = Bytes.toBytes("v1");
1430      p.addColumn(Bytes.toBytes("f"), q1, v1);
1431      table.put(p);
1432    }
1433    this.admin.flush(tableName);
1434    try {
1435      this.admin.split(tableName, Bytes.toBytes("row5"));
1436      Threads.sleep(10000);
1437    } catch (Exception e) {
1438      // Nothing to do.
1439    }
1440    // Split should not happen.
1441    List<RegionInfo> allRegions = MetaTableAccessor.getTableRegions(
1442        this.admin.getConnection(), tableName, true);
1443    assertEquals(1, allRegions.size());
1444  }
1445}