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