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