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}