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