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.assertThrows; 023import static org.junit.Assert.assertTrue; 024import static org.junit.Assert.fail; 025 026import java.io.IOException; 027import java.util.ArrayList; 028import java.util.Collection; 029import java.util.Collections; 030import java.util.EnumSet; 031import java.util.HashMap; 032import java.util.HashSet; 033import java.util.List; 034import java.util.concurrent.ThreadLocalRandom; 035import java.util.concurrent.atomic.AtomicInteger; 036import java.util.stream.Collectors; 037import org.apache.hadoop.conf.Configuration; 038import org.apache.hadoop.hbase.ClusterMetrics.Option; 039import org.apache.hadoop.hbase.HBaseClassTestRule; 040import org.apache.hadoop.hbase.HColumnDescriptor; 041import org.apache.hadoop.hbase.HConstants; 042import org.apache.hadoop.hbase.HRegionLocation; 043import org.apache.hadoop.hbase.HTableDescriptor; 044import org.apache.hadoop.hbase.MiniHBaseCluster; 045import org.apache.hadoop.hbase.ServerName; 046import org.apache.hadoop.hbase.TableExistsException; 047import org.apache.hadoop.hbase.TableName; 048import org.apache.hadoop.hbase.TableNotDisabledException; 049import org.apache.hadoop.hbase.TableNotEnabledException; 050import org.apache.hadoop.hbase.TableNotFoundException; 051import org.apache.hadoop.hbase.UnknownRegionException; 052import org.apache.hadoop.hbase.Waiter.Predicate; 053import org.apache.hadoop.hbase.ZooKeeperConnectionException; 054import org.apache.hadoop.hbase.constraint.ConstraintException; 055import org.apache.hadoop.hbase.ipc.HBaseRpcController; 056import org.apache.hadoop.hbase.master.HMaster; 057import org.apache.hadoop.hbase.master.assignment.AssignmentManager; 058import org.apache.hadoop.hbase.regionserver.HRegion; 059import org.apache.hadoop.hbase.regionserver.HRegionServer; 060import org.apache.hadoop.hbase.regionserver.HStore; 061import org.apache.hadoop.hbase.testclassification.ClientTests; 062import org.apache.hadoop.hbase.testclassification.LargeTests; 063import org.apache.hadoop.hbase.util.Bytes; 064import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 065import org.apache.hadoop.hbase.util.Pair; 066import org.apache.hadoop.hbase.wal.AbstractFSWALProvider; 067import org.junit.Assert; 068import org.junit.ClassRule; 069import org.junit.Test; 070import org.junit.experimental.categories.Category; 071import org.slf4j.Logger; 072import org.slf4j.LoggerFactory; 073 074import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 075 076/** 077 * Class to test HBaseAdmin. Spins up the minicluster once at test start and then takes it down 078 * afterward. Add any testing of HBaseAdmin functionality here. 079 */ 080@Category({ LargeTests.class, ClientTests.class }) 081public class TestAdmin2 extends TestAdminBase { 082 083 @ClassRule 084 public static final HBaseClassTestRule CLASS_RULE = HBaseClassTestRule.forClass(TestAdmin2.class); 085 086 private static final Logger LOG = LoggerFactory.getLogger(TestAdmin2.class); 087 088 @Test 089 public void testCreateBadTables() throws IOException { 090 String msg = null; 091 try { 092 ADMIN.createTable(new HTableDescriptor(TableName.META_TABLE_NAME)); 093 } catch (TableExistsException e) { 094 msg = e.toString(); 095 } 096 assertTrue("Unexcepted exception message " + msg, 097 msg != null && msg.startsWith(TableExistsException.class.getName()) 098 && msg.contains(TableName.META_TABLE_NAME.getNameAsString())); 099 100 // Now try and do concurrent creation with a bunch of threads. 101 final HTableDescriptor threadDesc = 102 new HTableDescriptor(TableName.valueOf(name.getMethodName())); 103 threadDesc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY)); 104 int count = 10; 105 Thread[] threads = new Thread[count]; 106 final AtomicInteger successes = new AtomicInteger(0); 107 final AtomicInteger failures = new AtomicInteger(0); 108 final Admin localAdmin = ADMIN; 109 for (int i = 0; i < count; i++) { 110 threads[i] = new Thread(Integer.toString(i)) { 111 @Override 112 public void run() { 113 try { 114 localAdmin.createTable(threadDesc); 115 successes.incrementAndGet(); 116 } catch (TableExistsException e) { 117 failures.incrementAndGet(); 118 } catch (IOException e) { 119 throw new RuntimeException("Failed threaded create" + getName(), e); 120 } 121 } 122 }; 123 } 124 for (int i = 0; i < count; i++) { 125 threads[i].start(); 126 } 127 for (int i = 0; i < count; i++) { 128 while (threads[i].isAlive()) { 129 try { 130 Thread.sleep(100); 131 } catch (InterruptedException e) { 132 // continue 133 } 134 } 135 } 136 // All threads are now dead. Count up how many tables were created and 137 // how many failed w/ appropriate exception. 138 assertEquals(1, successes.get()); 139 assertEquals(count - 1, failures.get()); 140 } 141 142 /** 143 * Test for hadoop-1581 'HBASE: Unopenable tablename bug'. 144 */ 145 @Test 146 public void testTableNameClash() throws Exception { 147 final String name = this.name.getMethodName(); 148 HTableDescriptor htd1 = new HTableDescriptor(TableName.valueOf(name + "SOMEUPPERCASE")); 149 HTableDescriptor htd2 = new HTableDescriptor(TableName.valueOf(name)); 150 htd1.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY)); 151 htd2.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY)); 152 ADMIN.createTable(htd1); 153 ADMIN.createTable(htd2); 154 // Before fix, below would fail throwing a NoServerForRegionException. 155 TEST_UTIL.getConnection().getTable(htd2.getTableName()).close(); 156 } 157 158 /*** 159 * HMaster.createTable used to be kind of synchronous call Thus creating of table with lots of 160 * regions can cause RPC timeout After the fix to make createTable truly async, RPC timeout 161 * shouldn't be an issue anymore 162 */ 163 @Test 164 public void testCreateTableRPCTimeOut() throws Exception { 165 final String name = this.name.getMethodName(); 166 int oldTimeout = TEST_UTIL.getConfiguration().getInt(HConstants.HBASE_RPC_TIMEOUT_KEY, 167 HConstants.DEFAULT_HBASE_RPC_TIMEOUT); 168 TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, 1500); 169 try { 170 int expectedRegions = 100; 171 // Use 80 bit numbers to make sure we aren't limited 172 byte[] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; 173 byte[] endKey = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; 174 Admin hbaseadmin = TEST_UTIL.getHBaseAdmin(); 175 HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name)); 176 htd.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY)); 177 hbaseadmin.createTable(htd, startKey, endKey, expectedRegions); 178 } finally { 179 TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, oldTimeout); 180 } 181 } 182 183 /** 184 * Test read only tables 185 */ 186 @Test 187 public void testReadOnlyTable() throws Exception { 188 final TableName name = TableName.valueOf(this.name.getMethodName()); 189 Table table = TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY); 190 byte[] value = Bytes.toBytes("somedata"); 191 // This used to use an empty row... That must have been a bug 192 Put put = new Put(value); 193 put.addColumn(HConstants.CATALOG_FAMILY, HConstants.CATALOG_FAMILY, value); 194 table.put(put); 195 table.close(); 196 } 197 198 /** 199 * Test that user table names can contain '-' and '.' so long as they do not start with same. 200 * HBASE-771 201 */ 202 @Test 203 public void testTableNames() throws IOException { 204 byte[][] illegalNames = new byte[][] { Bytes.toBytes("-bad"), Bytes.toBytes(".bad") }; 205 for (byte[] illegalName : illegalNames) { 206 try { 207 new HTableDescriptor(TableName.valueOf(illegalName)); 208 throw new IOException( 209 "Did not detect '" + Bytes.toString(illegalName) + "' as an illegal user table name"); 210 } catch (IllegalArgumentException e) { 211 // expected 212 } 213 } 214 byte[] legalName = Bytes.toBytes("g-oo.d"); 215 try { 216 new HTableDescriptor(TableName.valueOf(legalName)); 217 } catch (IllegalArgumentException e) { 218 throw new IOException("Legal user table name: '" + Bytes.toString(legalName) 219 + "' caused IllegalArgumentException: " + e.getMessage()); 220 } 221 } 222 223 /** 224 * For HADOOP-2579 225 */ 226 @Test(expected = TableExistsException.class) 227 public void testTableExistsExceptionWithATable() throws IOException { 228 final TableName name = TableName.valueOf(this.name.getMethodName()); 229 TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY).close(); 230 TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY); 231 } 232 233 /** 234 * Can't disable a table if the table isn't in enabled state 235 */ 236 @Test(expected = TableNotEnabledException.class) 237 public void testTableNotEnabledExceptionWithATable() throws IOException { 238 final TableName name = TableName.valueOf(this.name.getMethodName()); 239 TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY).close(); 240 ADMIN.disableTable(name); 241 ADMIN.disableTable(name); 242 } 243 244 /** 245 * Can't enable a table if the table isn't in disabled state 246 */ 247 @Test(expected = TableNotDisabledException.class) 248 public void testTableNotDisabledExceptionWithATable() throws IOException { 249 final TableName name = TableName.valueOf(this.name.getMethodName()); 250 Table t = TEST_UTIL.createTable(name, HConstants.CATALOG_FAMILY); 251 try { 252 ADMIN.enableTable(name); 253 } finally { 254 t.close(); 255 } 256 } 257 258 /** 259 * For HADOOP-2579 260 */ 261 @Test(expected = TableNotFoundException.class) 262 public void testTableNotFoundExceptionWithoutAnyTables() throws IOException { 263 TableName tableName = TableName.valueOf("testTableNotFoundExceptionWithoutAnyTables"); 264 Table ht = TEST_UTIL.getConnection().getTable(tableName); 265 ht.get(new Get(Bytes.toBytes("e"))); 266 } 267 268 @Test 269 public void testShouldUnassignTheRegion() throws Exception { 270 final TableName tableName = TableName.valueOf(name.getMethodName()); 271 createTableWithDefaultConf(tableName); 272 273 RegionInfo info = null; 274 HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(tableName); 275 List<RegionInfo> onlineRegions = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices()); 276 for (RegionInfo regionInfo : onlineRegions) { 277 if (!regionInfo.getTable().isSystemTable()) { 278 info = regionInfo; 279 ADMIN.unassign(regionInfo.getRegionName(), true); 280 } 281 } 282 boolean isInList = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices()).contains(info); 283 long timeout = EnvironmentEdgeManager.currentTime() + 10000; 284 while ((EnvironmentEdgeManager.currentTime() < timeout) && (isInList)) { 285 Thread.sleep(100); 286 isInList = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices()).contains(info); 287 } 288 289 assertFalse("The region should not be present in online regions list.", isInList); 290 } 291 292 @Test 293 public void testCloseRegionIfInvalidRegionNameIsPassed() throws Exception { 294 final String name = this.name.getMethodName(); 295 byte[] tableName = Bytes.toBytes(name); 296 createTableWithDefaultConf(tableName); 297 298 RegionInfo info = null; 299 HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TableName.valueOf(tableName)); 300 List<RegionInfo> onlineRegions = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices()); 301 for (RegionInfo regionInfo : onlineRegions) { 302 if (!regionInfo.isMetaRegion()) { 303 if (regionInfo.getRegionNameAsString().contains(name)) { 304 info = regionInfo; 305 assertThrows(UnknownRegionException.class, () -> ADMIN.unassign( 306 Bytes.toBytes("test,,1358563771069.acc1ad1b7962564fc3a43e5907e8db33."), true)); 307 } 308 } 309 } 310 onlineRegions = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices()); 311 assertTrue("The region should be present in online regions list.", 312 onlineRegions.contains(info)); 313 } 314 315 @Test 316 public void testCloseRegionThatFetchesTheHRIFromMeta() throws Exception { 317 final TableName tableName = TableName.valueOf(name.getMethodName()); 318 createTableWithDefaultConf(tableName); 319 320 RegionInfo info = null; 321 HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(tableName); 322 List<RegionInfo> onlineRegions = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices()); 323 for (RegionInfo regionInfo : onlineRegions) { 324 if (!regionInfo.isMetaRegion()) { 325 if (regionInfo.getRegionNameAsString().contains("TestHBACloseRegion2")) { 326 info = regionInfo; 327 ADMIN.unassign(regionInfo.getRegionName(), true); 328 } 329 } 330 } 331 332 boolean isInList = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices()).contains(info); 333 long timeout = EnvironmentEdgeManager.currentTime() + 10000; 334 while ((EnvironmentEdgeManager.currentTime() < timeout) && (isInList)) { 335 Thread.sleep(100); 336 isInList = ProtobufUtil.getOnlineRegions(rs.getRSRpcServices()).contains(info); 337 } 338 339 assertFalse("The region should not be present in online regions list.", isInList); 340 } 341 342 private HBaseAdmin createTable(TableName tableName) throws IOException { 343 HBaseAdmin admin = TEST_UTIL.getHBaseAdmin(); 344 345 HTableDescriptor htd = new HTableDescriptor(tableName); 346 HColumnDescriptor hcd = new HColumnDescriptor("value"); 347 348 htd.addFamily(hcd); 349 admin.createTable(htd, null); 350 return admin; 351 } 352 353 private void createTableWithDefaultConf(byte[] TABLENAME) throws IOException { 354 createTableWithDefaultConf(TableName.valueOf(TABLENAME)); 355 } 356 357 private void createTableWithDefaultConf(TableName TABLENAME) throws IOException { 358 HTableDescriptor htd = new HTableDescriptor(TABLENAME); 359 HColumnDescriptor hcd = new HColumnDescriptor("value"); 360 htd.addFamily(hcd); 361 362 ADMIN.createTable(htd, null); 363 } 364 365 /** 366 * For HBASE-2556 367 */ 368 @Test 369 public void testGetTableRegions() throws IOException { 370 final TableName tableName = TableName.valueOf(name.getMethodName()); 371 372 int expectedRegions = 10; 373 374 // Use 80 bit numbers to make sure we aren't limited 375 byte[] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; 376 byte[] endKey = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; 377 378 HTableDescriptor desc = new HTableDescriptor(tableName); 379 desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY)); 380 ADMIN.createTable(desc, startKey, endKey, expectedRegions); 381 382 List<RegionInfo> RegionInfos = ADMIN.getRegions(tableName); 383 384 assertEquals( 385 "Tried to create " + expectedRegions + " regions " + "but only found " + RegionInfos.size(), 386 expectedRegions, RegionInfos.size()); 387 } 388 389 @Test 390 public void testMoveToPreviouslyAssignedRS() throws IOException, InterruptedException { 391 MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 392 HMaster master = cluster.getMaster(); 393 final TableName tableName = TableName.valueOf(name.getMethodName()); 394 Admin localAdmin = createTable(tableName); 395 List<RegionInfo> tableRegions = localAdmin.getRegions(tableName); 396 RegionInfo hri = tableRegions.get(0); 397 AssignmentManager am = master.getAssignmentManager(); 398 ServerName server = am.getRegionStates().getRegionServerOfRegion(hri); 399 localAdmin.move(hri.getEncodedNameAsBytes(), server); 400 assertEquals("Current region server and region server before move should be same.", server, 401 am.getRegionStates().getRegionServerOfRegion(hri)); 402 } 403 404 @Test 405 public void testWALRollWriting() throws Exception { 406 setUpforLogRolling(); 407 String className = this.getClass().getName(); 408 StringBuilder v = new StringBuilder(className); 409 while (v.length() < 1000) { 410 v.append(className); 411 } 412 byte[] value = Bytes.toBytes(v.toString()); 413 HRegionServer regionServer = startAndWriteData(TableName.valueOf(name.getMethodName()), value); 414 LOG.info("after writing there are " 415 + AbstractFSWALProvider.getNumRolledLogFiles(regionServer.getWAL(null)) + " log files"); 416 417 // flush all regions 418 for (HRegion r : regionServer.getOnlineRegionsLocalContext()) { 419 r.flush(true); 420 } 421 ADMIN.rollWALWriter(regionServer.getServerName()); 422 TEST_UTIL.waitFor(5000, () -> { 423 int count = AbstractFSWALProvider.getNumRolledLogFiles(regionServer.getWAL(null)); 424 LOG.info("after flushing all regions and rolling logs there are " + count + " log files"); 425 return count <= 2; 426 }); 427 } 428 429 private void setUpforLogRolling() { 430 // Force a region split after every 768KB 431 TEST_UTIL.getConfiguration().setLong(HConstants.HREGION_MAX_FILESIZE, 768L * 1024L); 432 433 // We roll the log after every 32 writes 434 TEST_UTIL.getConfiguration().setInt("hbase.regionserver.maxlogentries", 32); 435 436 TEST_UTIL.getConfiguration().setInt("hbase.regionserver.logroll.errors.tolerated", 2); 437 TEST_UTIL.getConfiguration().setInt("hbase.rpc.timeout", 10 * 1000); 438 439 // For less frequently updated regions flush after every 2 flushes 440 TEST_UTIL.getConfiguration().setInt("hbase.hregion.memstore.optionalflushcount", 2); 441 442 // We flush the cache after every 8192 bytes 443 TEST_UTIL.getConfiguration().setInt(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, 8192); 444 445 // Increase the amount of time between client retries 446 TEST_UTIL.getConfiguration().setLong("hbase.client.pause", 10 * 1000); 447 448 // Reduce thread wake frequency so that other threads can get 449 // a chance to run. 450 TEST_UTIL.getConfiguration().setInt(HConstants.THREAD_WAKE_FREQUENCY, 2 * 1000); 451 452 /**** configuration for testLogRollOnDatanodeDeath ****/ 453 // lower the namenode & datanode heartbeat so the namenode 454 // quickly detects datanode failures 455 TEST_UTIL.getConfiguration().setInt("dfs.namenode.heartbeat.recheck-interval", 5000); 456 TEST_UTIL.getConfiguration().setInt("dfs.heartbeat.interval", 1); 457 // the namenode might still try to choose the recently-dead datanode 458 // for a pipeline, so try to a new pipeline multiple times 459 TEST_UTIL.getConfiguration().setInt("dfs.client.block.write.retries", 30); 460 TEST_UTIL.getConfiguration().setInt("hbase.regionserver.hlog.tolerable.lowreplication", 2); 461 TEST_UTIL.getConfiguration().setInt("hbase.regionserver.hlog.lowreplication.rolllimit", 3); 462 } 463 464 private HRegionServer startAndWriteData(TableName tableName, byte[] value) 465 throws IOException, InterruptedException { 466 // When the hbase:meta table can be opened, the region servers are running 467 TEST_UTIL.getConnection().getTable(TableName.META_TABLE_NAME).close(); 468 469 // Create the test table and open it 470 HTableDescriptor desc = new HTableDescriptor(tableName); 471 desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY)); 472 ADMIN.createTable(desc); 473 Table table = TEST_UTIL.getConnection().getTable(tableName); 474 475 HRegionServer regionServer = TEST_UTIL.getRSForFirstRegionInTable(tableName); 476 for (int i = 1; i <= 256; i++) { // 256 writes should cause 8 log rolls 477 Put put = new Put(Bytes.toBytes("row" + String.format("%1$04d", i))); 478 put.addColumn(HConstants.CATALOG_FAMILY, null, value); 479 table.put(put); 480 if (i % 32 == 0) { 481 // After every 32 writes sleep to let the log roller run 482 try { 483 Thread.sleep(2000); 484 } catch (InterruptedException e) { 485 // continue 486 } 487 } 488 } 489 490 table.close(); 491 return regionServer; 492 } 493 494 /** 495 * Check that we have an exception if the cluster is not there. 496 */ 497 @Test 498 public void testCheckHBaseAvailableWithoutCluster() { 499 Configuration conf = new Configuration(TEST_UTIL.getConfiguration()); 500 501 // Test makes sense only when ZK connection registry is in use. 502 conf.set(HConstants.CLIENT_CONNECTION_REGISTRY_IMPL_CONF_KEY, 503 HConstants.ZK_CONNECTION_REGISTRY_CLASS); 504 // Change the ZK address to go to something not used. 505 conf.setInt(HConstants.ZOOKEEPER_CLIENT_PORT, 506 conf.getInt(HConstants.ZOOKEEPER_CLIENT_PORT, 9999) + 10); 507 508 long start = EnvironmentEdgeManager.currentTime(); 509 try { 510 HBaseAdmin.available(conf); 511 assertTrue(false); 512 } catch (ZooKeeperConnectionException ignored) { 513 } catch (IOException ignored) { 514 } 515 long end = EnvironmentEdgeManager.currentTime(); 516 517 LOG.info("It took " + (end - start) + " ms to find out that" + " HBase was not available"); 518 } 519 520 @Test 521 public void testDisableCatalogTable() throws Exception { 522 try { 523 ADMIN.disableTable(TableName.META_TABLE_NAME); 524 fail("Expected to throw ConstraintException"); 525 } catch (ConstraintException e) { 526 } 527 // Before the fix for HBASE-6146, the below table creation was failing as the hbase:meta table 528 // actually getting disabled by the disableTable() call. 529 HTableDescriptor htd = 530 new HTableDescriptor(TableName.valueOf(Bytes.toBytes(name.getMethodName()))); 531 HColumnDescriptor hcd = new HColumnDescriptor(Bytes.toBytes("cf1")); 532 htd.addFamily(hcd); 533 TEST_UTIL.getHBaseAdmin().createTable(htd); 534 } 535 536 @Test 537 public void testIsEnabledOrDisabledOnUnknownTable() throws Exception { 538 try { 539 ADMIN.isTableEnabled(TableName.valueOf(name.getMethodName())); 540 fail("Test should fail if isTableEnabled called on unknown table."); 541 } catch (IOException e) { 542 } 543 544 try { 545 ADMIN.isTableDisabled(TableName.valueOf(name.getMethodName())); 546 fail("Test should fail if isTableDisabled called on unknown table."); 547 } catch (IOException e) { 548 } 549 } 550 551 @Test 552 public void testGetRegion() throws Exception { 553 // We use actual HBaseAdmin instance instead of going via Admin interface in 554 // here because makes use of an internal HBA method (TODO: Fix.). 555 HBaseAdmin rawAdmin = TEST_UTIL.getHBaseAdmin(); 556 557 final TableName tableName = TableName.valueOf(name.getMethodName()); 558 LOG.info("Started " + tableName); 559 Table t = TEST_UTIL.createMultiRegionTable(tableName, HConstants.CATALOG_FAMILY); 560 561 try (RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName)) { 562 HRegionLocation regionLocation = locator.getRegionLocation(Bytes.toBytes("mmm")); 563 RegionInfo region = regionLocation.getRegionInfo(); 564 byte[] regionName = region.getRegionName(); 565 Pair<RegionInfo, ServerName> pair = rawAdmin.getRegion(regionName); 566 assertTrue(Bytes.equals(regionName, pair.getFirst().getRegionName())); 567 pair = rawAdmin.getRegion(region.getEncodedNameAsBytes()); 568 assertTrue(Bytes.equals(regionName, pair.getFirst().getRegionName())); 569 } 570 } 571 572 @Test 573 public void testBalancer() throws Exception { 574 boolean initialState = ADMIN.isBalancerEnabled(); 575 576 // Start the balancer, wait for it. 577 boolean prevState = ADMIN.setBalancerRunning(!initialState, true); 578 579 // The previous state should be the original state we observed 580 assertEquals(initialState, prevState); 581 582 // Current state should be opposite of the original 583 assertEquals(!initialState, ADMIN.isBalancerEnabled()); 584 585 // Reset it back to what it was 586 prevState = ADMIN.setBalancerRunning(initialState, true); 587 588 // The previous state should be the opposite of the initial state 589 assertEquals(!initialState, prevState); 590 // Current state should be the original state again 591 assertEquals(initialState, ADMIN.isBalancerEnabled()); 592 } 593 594 @Test 595 public void testRegionNormalizer() throws Exception { 596 boolean initialState = ADMIN.isNormalizerEnabled(); 597 598 // flip state 599 boolean prevState = ADMIN.setNormalizerRunning(!initialState); 600 601 // The previous state should be the original state we observed 602 assertEquals(initialState, prevState); 603 604 // Current state should be opposite of the original 605 assertEquals(!initialState, ADMIN.isNormalizerEnabled()); 606 607 // Reset it back to what it was 608 prevState = ADMIN.setNormalizerRunning(initialState); 609 610 // The previous state should be the opposite of the initial state 611 assertEquals(!initialState, prevState); 612 // Current state should be the original state again 613 assertEquals(initialState, ADMIN.isNormalizerEnabled()); 614 } 615 616 @Test 617 public void testAbortProcedureFail() throws Exception { 618 long procId = ThreadLocalRandom.current().nextLong(); 619 boolean abortResult = ADMIN.abortProcedure(procId, true); 620 assertFalse(abortResult); 621 } 622 623 @Test 624 public void testGetProcedures() throws Exception { 625 String procList = ADMIN.getProcedures(); 626 assertTrue(procList.startsWith("[")); 627 } 628 629 @Test 630 public void testGetLocks() throws Exception { 631 String lockList = ADMIN.getLocks(); 632 assertTrue(lockList.startsWith("[")); 633 } 634 635 @Test 636 public void testDecommissionRegionServers() throws Exception { 637 List<ServerName> decommissionedRegionServers = ADMIN.listDecommissionedRegionServers(); 638 assertTrue(decommissionedRegionServers.isEmpty()); 639 640 final TableName tableName = TableName.valueOf(name.getMethodName()); 641 TEST_UTIL.createMultiRegionTable(tableName, Bytes.toBytes("f"), 6); 642 643 ArrayList<ServerName> clusterRegionServers = new ArrayList<>( 644 ADMIN.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)).getLiveServerMetrics().keySet()); 645 646 assertEquals(3, clusterRegionServers.size()); 647 648 HashMap<ServerName, List<RegionInfo>> serversToDecommssion = new HashMap<>(); 649 // Get a server that has meta online. We will decommission two of the servers, 650 // leaving one online. 651 int i; 652 for (i = 0; i < clusterRegionServers.size(); i++) { 653 List<RegionInfo> regionsOnServer = ADMIN.getRegions(clusterRegionServers.get(i)); 654 if (ADMIN.getRegions(clusterRegionServers.get(i)).stream().anyMatch(p -> p.isMetaRegion())) { 655 serversToDecommssion.put(clusterRegionServers.get(i), regionsOnServer); 656 break; 657 } 658 } 659 660 clusterRegionServers.remove(i); 661 // Get another server to decommission. 662 serversToDecommssion.put(clusterRegionServers.get(0), 663 ADMIN.getRegions(clusterRegionServers.get(0))); 664 665 ServerName remainingServer = clusterRegionServers.get(1); 666 667 // Decommission 668 ADMIN.decommissionRegionServers(new ArrayList<ServerName>(serversToDecommssion.keySet()), true); 669 assertEquals(2, ADMIN.listDecommissionedRegionServers().size()); 670 671 // Verify the regions have been off the decommissioned servers, all on the one 672 // remaining server. 673 for (ServerName server : serversToDecommssion.keySet()) { 674 for (RegionInfo region : serversToDecommssion.get(server)) { 675 TEST_UTIL.assertRegionOnServer(region, remainingServer, 10000); 676 } 677 } 678 679 // Recommission and load the regions. 680 for (ServerName server : serversToDecommssion.keySet()) { 681 List<byte[]> encodedRegionNames = serversToDecommssion.get(server).stream() 682 .map(region -> region.getEncodedNameAsBytes()).collect(Collectors.toList()); 683 ADMIN.recommissionRegionServer(server, encodedRegionNames); 684 } 685 assertTrue(ADMIN.listDecommissionedRegionServers().isEmpty()); 686 // Verify the regions have been moved to the recommissioned servers 687 for (ServerName server : serversToDecommssion.keySet()) { 688 for (RegionInfo region : serversToDecommssion.get(server)) { 689 TEST_UTIL.assertRegionOnServer(region, server, 10000); 690 } 691 } 692 } 693 694 /** 695 * TestCase for HBASE-21355 696 */ 697 @Test 698 public void testGetRegionInfo() throws Exception { 699 final TableName tableName = TableName.valueOf(name.getMethodName()); 700 Table table = TEST_UTIL.createTable(tableName, Bytes.toBytes("f")); 701 for (int i = 0; i < 100; i++) { 702 table.put(new Put(Bytes.toBytes(i)).addColumn(Bytes.toBytes("f"), Bytes.toBytes("q"), 703 Bytes.toBytes(i))); 704 } 705 ADMIN.flush(tableName); 706 707 HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(table.getName()); 708 List<HRegion> regions = rs.getRegions(tableName); 709 Assert.assertEquals(1, regions.size()); 710 711 HRegion region = regions.get(0); 712 byte[] regionName = region.getRegionInfo().getRegionName(); 713 HStore store = region.getStore(Bytes.toBytes("f")); 714 long expectedStoreFilesSize = store.getStorefilesSize(); 715 Assert.assertNotNull(store); 716 Assert.assertEquals(expectedStoreFilesSize, store.getSize()); 717 718 ClusterConnection conn = ((ClusterConnection) ADMIN.getConnection()); 719 HBaseRpcController controller = conn.getRpcControllerFactory().newController(); 720 for (int i = 0; i < 10; i++) { 721 RegionInfo ri = 722 ProtobufUtil.getRegionInfo(controller, conn.getAdmin(rs.getServerName()), regionName); 723 Assert.assertEquals(region.getRegionInfo(), ri); 724 725 // Make sure that the store size is still the actual file system's store size. 726 Assert.assertEquals(expectedStoreFilesSize, store.getSize()); 727 } 728 729 // Test querying using the encoded name only. When encoded name passed, 730 // and the target server is the Master, we return the full region name. 731 // Convenience. 732 testGetWithEncodedRegionName(conn, region.getRegionInfo()); 733 testGetWithRegionName(conn, region.getRegionInfo()); 734 // Try querying meta encoded name. 735 testGetWithEncodedRegionName(conn, RegionInfoBuilder.FIRST_META_REGIONINFO); 736 testGetWithRegionName(conn, RegionInfoBuilder.FIRST_META_REGIONINFO); 737 } 738 739 /** 740 * Do get of RegionInfo from Master using encoded region name. 741 */ 742 private void testGetWithEncodedRegionName(ClusterConnection conn, RegionInfo inputRI) 743 throws IOException { 744 RegionInfo ri = ProtobufUtil.getRegionInfo(null, 745 conn.getAdmin(TEST_UTIL.getMiniHBaseCluster().getMaster().getServerName()), 746 inputRI.getEncodedNameAsBytes()); 747 assertEquals(inputRI, ri); 748 } 749 750 private void testGetWithRegionName(ClusterConnection conn, RegionInfo inputRI) 751 throws IOException { 752 RegionInfo ri = ProtobufUtil.getRegionInfo(null, 753 conn.getAdmin(TEST_UTIL.getMiniHBaseCluster().getMaster().getServerName()), 754 inputRI.getRegionName()); 755 assertEquals(inputRI, ri); 756 } 757 758 @Test 759 public void testTableSplitFollowedByModify() throws Exception { 760 final TableName tableName = TableName.valueOf(name.getMethodName()); 761 TEST_UTIL.createTable(tableName, Bytes.toBytes("f")); 762 763 // get the original table region count 764 List<RegionInfo> regions = ADMIN.getRegions(tableName); 765 int originalCount = regions.size(); 766 assertEquals(1, originalCount); 767 768 // split the table and wait until region count increases 769 ADMIN.split(tableName, Bytes.toBytes(3)); 770 TEST_UTIL.waitFor(30000, new Predicate<Exception>() { 771 772 @Override 773 public boolean evaluate() throws Exception { 774 return ADMIN.getRegions(tableName).size() > originalCount; 775 } 776 }); 777 778 // do some table modification 779 TableDescriptor tableDesc = TableDescriptorBuilder.newBuilder(ADMIN.getDescriptor(tableName)) 780 .setMaxFileSize(11111111).build(); 781 ADMIN.modifyTable(tableDesc); 782 assertEquals(11111111, ADMIN.getDescriptor(tableName).getMaxFileSize()); 783 } 784 785 @SuppressWarnings("FutureReturnValueIgnored") 786 @Test 787 public void testTableMergeFollowedByModify() throws Exception { 788 final TableName tableName = TableName.valueOf(name.getMethodName()); 789 TEST_UTIL.createTable(tableName, new byte[][] { Bytes.toBytes("f") }, 790 new byte[][] { Bytes.toBytes(3) }); 791 792 // assert we have at least 2 regions in the table 793 List<RegionInfo> regions = ADMIN.getRegions(tableName); 794 int originalCount = regions.size(); 795 assertTrue(originalCount >= 2); 796 797 byte[] nameOfRegionA = regions.get(0).getEncodedNameAsBytes(); 798 byte[] nameOfRegionB = regions.get(1).getEncodedNameAsBytes(); 799 800 // merge the table regions and wait until region count decreases 801 ADMIN.mergeRegionsAsync(nameOfRegionA, nameOfRegionB, true); 802 TEST_UTIL.waitFor(30000, new Predicate<Exception>() { 803 804 @Override 805 public boolean evaluate() throws Exception { 806 return ADMIN.getRegions(tableName).size() < originalCount; 807 } 808 }); 809 810 // do some table modification 811 TableDescriptor tableDesc = TableDescriptorBuilder.newBuilder(ADMIN.getDescriptor(tableName)) 812 .setMaxFileSize(11111111).build(); 813 ADMIN.modifyTable(tableDesc); 814 assertEquals(11111111, ADMIN.getDescriptor(tableName).getMaxFileSize()); 815 } 816 817 @Test 818 public void testSnapshotCleanupAsync() throws Exception { 819 testSnapshotCleanup(false); 820 } 821 822 @Test 823 public void testSnapshotCleanupSync() throws Exception { 824 testSnapshotCleanup(true); 825 } 826 827 private void testSnapshotCleanup(final boolean synchronous) throws IOException { 828 final boolean initialState = ADMIN.isSnapshotCleanupEnabled(); 829 // Switch the snapshot auto cleanup state to opposite to initial state 830 boolean prevState = ADMIN.snapshotCleanupSwitch(!initialState, synchronous); 831 // The previous state should be the original state we observed 832 assertEquals(initialState, prevState); 833 // Current state should be opposite of the initial state 834 assertEquals(!initialState, ADMIN.isSnapshotCleanupEnabled()); 835 // Reset the state back to what it was initially 836 prevState = ADMIN.snapshotCleanupSwitch(initialState, synchronous); 837 // The previous state should be the opposite of the initial state 838 assertEquals(!initialState, prevState); 839 // Current state should be the original state again 840 assertEquals(initialState, ADMIN.isSnapshotCleanupEnabled()); 841 } 842 843 @Test 844 public void testSlowLogResponses() throws Exception { 845 // get all live server names 846 Collection<ServerName> serverNames = ADMIN.getRegionServers(); 847 List<ServerName> serverNameList = new ArrayList<>(serverNames); 848 849 // clean up slowlog responses maintained in memory by RegionServers 850 List<Boolean> areSlowLogsCleared = ADMIN.clearSlowLogResponses(new HashSet<>(serverNameList)); 851 852 int countFailedClearSlowResponse = 0; 853 for (Boolean isSlowLogCleared : areSlowLogsCleared) { 854 if (!isSlowLogCleared) { 855 ++countFailedClearSlowResponse; 856 } 857 } 858 Assert.assertEquals(countFailedClearSlowResponse, 0); 859 860 List<LogEntry> onlineLogRecords = ADMIN.getLogEntries(new HashSet<>(serverNames), "SLOW_LOG", 861 ServerType.REGION_SERVER, 100, null); 862 // after cleanup of slowlog responses, total count of slowlog payloads should be 0 863 Assert.assertEquals(onlineLogRecords.size(), 0); 864 List<LogEntry> balancerDecisionRecords = 865 ADMIN.getLogEntries(null, "BALANCER_DECISION", ServerType.MASTER, 100, null); 866 Assert.assertEquals(balancerDecisionRecords.size(), 0); 867 } 868 869 @Test 870 public void testGetRegionServers() throws Exception { 871 // get all live server names 872 List<ServerName> serverNames = new ArrayList<>(ADMIN.getRegionServers(true)); 873 Assert.assertEquals(3, serverNames.size()); 874 875 List<ServerName> serversToDecom = new ArrayList<>(); 876 ServerName serverToDecommission = serverNames.get(0); 877 878 serversToDecom.add(serverToDecommission); 879 ADMIN.decommissionRegionServers(serversToDecom, false); 880 waitForServerCommissioned(serverToDecommission, true); 881 882 Assert.assertEquals(2, ADMIN.getRegionServers(true).size()); 883 Assert.assertEquals(3, ADMIN.getRegionServers(false).size()); 884 885 ADMIN.recommissionRegionServer(serverToDecommission, Collections.emptyList()); 886 waitForServerCommissioned(null, false); 887 888 Assert.assertEquals(3, ADMIN.getRegionServers(true).size()); 889 Assert.assertEquals(3, ADMIN.getRegionServers(false).size()); 890 } 891 892 private static void waitForServerCommissioned(ServerName excludeServer, 893 boolean anyServerDecommissioned) { 894 TEST_UTIL.waitFor(3000, () -> { 895 try { 896 List<ServerName> decomServers = TEST_UTIL.getAdmin().listDecommissionedRegionServers(); 897 if (anyServerDecommissioned) { 898 return decomServers.size() == 1 && decomServers.get(0).equals(excludeServer); 899 } else { 900 return decomServers.size() == 0; 901 } 902 } catch (IOException e) { 903 throw new RuntimeException(e); 904 } 905 }); 906 } 907 908}