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