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