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.rsgroup; 019 020import static org.apache.hadoop.hbase.util.Threads.sleep; 021import static org.junit.Assert.assertEquals; 022import static org.junit.Assert.assertFalse; 023import static org.junit.Assert.assertTrue; 024import static org.junit.Assert.fail; 025 026import java.io.IOException; 027import java.util.ArrayList; 028import java.util.EnumSet; 029import java.util.Iterator; 030import java.util.List; 031import java.util.Map; 032import java.util.Set; 033import java.util.concurrent.atomic.AtomicBoolean; 034import org.apache.hadoop.hbase.ClusterMetrics.Option; 035import org.apache.hadoop.hbase.HBaseClassTestRule; 036import org.apache.hadoop.hbase.ServerName; 037import org.apache.hadoop.hbase.TableName; 038import org.apache.hadoop.hbase.Waiter; 039import org.apache.hadoop.hbase.client.RegionInfo; 040import org.apache.hadoop.hbase.constraint.ConstraintException; 041import org.apache.hadoop.hbase.master.RegionState; 042import org.apache.hadoop.hbase.master.assignment.RegionStateNode; 043import org.apache.hadoop.hbase.net.Address; 044import org.apache.hadoop.hbase.testclassification.LargeTests; 045import org.apache.hadoop.hbase.util.Bytes; 046import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 047import org.junit.After; 048import org.junit.AfterClass; 049import org.junit.Assert; 050import org.junit.Before; 051import org.junit.BeforeClass; 052import org.junit.ClassRule; 053import org.junit.Test; 054import org.junit.experimental.categories.Category; 055import org.slf4j.Logger; 056import org.slf4j.LoggerFactory; 057 058import org.apache.hbase.thirdparty.com.google.common.collect.Sets; 059 060@Category({ LargeTests.class }) 061public class TestRSGroupsAdmin2 extends TestRSGroupsBase { 062 063 @ClassRule 064 public static final HBaseClassTestRule CLASS_RULE = 065 HBaseClassTestRule.forClass(TestRSGroupsAdmin2.class); 066 067 protected static final Logger LOG = LoggerFactory.getLogger(TestRSGroupsAdmin2.class); 068 069 @BeforeClass 070 public static void setUp() throws Exception { 071 setUpTestBeforeClass(); 072 } 073 074 @AfterClass 075 public static void tearDown() throws Exception { 076 tearDownAfterClass(); 077 } 078 079 @Before 080 public void beforeMethod() throws Exception { 081 setUpBeforeMethod(); 082 } 083 084 @After 085 public void afterMethod() throws Exception { 086 tearDownAfterMethod(); 087 } 088 089 @Test 090 public void testRegionMove() throws Exception { 091 final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 1); 092 final byte[] familyNameBytes = Bytes.toBytes("f"); 093 // All the regions created below will be assigned to the default group. 094 TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 6); 095 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 096 @Override 097 public boolean evaluate() throws Exception { 098 List<String> regions = getTableRegionMap().get(tableName); 099 if (regions == null) { 100 return false; 101 } 102 103 return getTableRegionMap().get(tableName).size() >= 6; 104 } 105 }); 106 107 // get target region to move 108 Map<ServerName, List<String>> assignMap = getTableServerRegionMap().get(tableName); 109 String targetRegion = null; 110 for (ServerName server : assignMap.keySet()) { 111 targetRegion = assignMap.get(server).size() > 0 ? assignMap.get(server).get(0) : null; 112 if (targetRegion != null) { 113 break; 114 } 115 } 116 // get server which is not a member of new group 117 ServerName tmpTargetServer = null; 118 for (ServerName server : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)) 119 .getLiveServerMetrics().keySet()) { 120 if (!newGroup.containsServer(server.getAddress())) { 121 tmpTargetServer = server; 122 break; 123 } 124 } 125 final ServerName targetServer = tmpTargetServer; 126 // move target server to group 127 rsGroupAdmin.moveServers(Sets.newHashSet(targetServer.getAddress()), newGroup.getName()); 128 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 129 @Override 130 public boolean evaluate() throws Exception { 131 return admin.getRegions(targetServer).size() <= 0; 132 } 133 }); 134 135 // Lets move this region to the new group. 136 TEST_UTIL.getAdmin() 137 .move(Bytes.toBytes(RegionInfo.encodeRegionName(Bytes.toBytes(targetRegion))), targetServer); 138 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 139 @Override 140 public boolean evaluate() throws Exception { 141 return getTableRegionMap().get(tableName) != null 142 && getTableRegionMap().get(tableName).size() == 6 143 && admin.getClusterMetrics(EnumSet.of(Option.REGIONS_IN_TRANSITION)) 144 .getRegionStatesInTransition().size() < 1; 145 } 146 }); 147 148 // verify that targetServer didn't open it 149 for (RegionInfo region : admin.getRegions(targetServer)) { 150 if (targetRegion.equals(region.getRegionNameAsString())) { 151 fail("Target server opened region"); 152 } 153 } 154 } 155 156 @Test 157 public void testRegionServerMove() throws IOException, InterruptedException { 158 int initNumGroups = rsGroupAdmin.listRSGroups().size(); 159 RSGroupInfo appInfo = addGroup(getGroupName(name.getMethodName()), 1); 160 RSGroupInfo adminInfo = addGroup(getGroupName(name.getMethodName()), 1); 161 RSGroupInfo dInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP); 162 Assert.assertEquals(initNumGroups + 2, rsGroupAdmin.listRSGroups().size()); 163 assertEquals(1, adminInfo.getServers().size()); 164 assertEquals(1, appInfo.getServers().size()); 165 assertEquals(getNumServers() - 2, dInfo.getServers().size()); 166 rsGroupAdmin.moveServers(appInfo.getServers(), RSGroupInfo.DEFAULT_GROUP); 167 rsGroupAdmin.removeRSGroup(appInfo.getName()); 168 rsGroupAdmin.moveServers(adminInfo.getServers(), RSGroupInfo.DEFAULT_GROUP); 169 rsGroupAdmin.removeRSGroup(adminInfo.getName()); 170 Assert.assertEquals(rsGroupAdmin.listRSGroups().size(), initNumGroups); 171 } 172 173 @Test 174 public void testMoveServers() throws Exception { 175 // create groups and assign servers 176 addGroup("bar", 3); 177 rsGroupAdmin.addRSGroup("foo"); 178 179 RSGroupInfo barGroup = rsGroupAdmin.getRSGroupInfo("bar"); 180 RSGroupInfo fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); 181 assertEquals(3, barGroup.getServers().size()); 182 assertEquals(0, fooGroup.getServers().size()); 183 184 // test fail bogus server move 185 try { 186 rsGroupAdmin.moveServers(Sets.newHashSet(Address.fromString("foo:9999")), "foo"); 187 fail("Bogus servers shouldn't have been successfully moved."); 188 } catch (IOException ex) { 189 String exp = "Server foo:9999 is either offline or it does not exist."; 190 String msg = "Expected '" + exp + "' in exception message: "; 191 assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp)); 192 } 193 194 // test success case 195 LOG.info("moving servers " + barGroup.getServers() + " to group foo"); 196 rsGroupAdmin.moveServers(barGroup.getServers(), fooGroup.getName()); 197 198 barGroup = rsGroupAdmin.getRSGroupInfo("bar"); 199 fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); 200 assertEquals(0, barGroup.getServers().size()); 201 assertEquals(3, fooGroup.getServers().size()); 202 203 LOG.info("moving servers " + fooGroup.getServers() + " to group default"); 204 rsGroupAdmin.moveServers(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP); 205 206 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 207 @Override 208 public boolean evaluate() throws Exception { 209 return getNumServers() 210 == rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size(); 211 } 212 }); 213 214 fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); 215 assertEquals(0, fooGroup.getServers().size()); 216 217 // test group removal 218 LOG.info("Remove group " + barGroup.getName()); 219 rsGroupAdmin.removeRSGroup(barGroup.getName()); 220 Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(barGroup.getName())); 221 LOG.info("Remove group " + fooGroup.getName()); 222 rsGroupAdmin.removeRSGroup(fooGroup.getName()); 223 Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(fooGroup.getName())); 224 } 225 226 @Test 227 public void testRemoveServers() throws Exception { 228 LOG.info("testRemoveServers"); 229 final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 3); 230 Iterator<Address> iterator = newGroup.getServers().iterator(); 231 ServerName targetServer = getServerName(iterator.next()); 232 233 // remove online servers 234 try { 235 rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress())); 236 fail("Online servers shouldn't have been successfully removed."); 237 } catch (IOException ex) { 238 String exp = 239 "Server " + targetServer.getAddress() + " is an online server, not allowed to remove."; 240 String msg = "Expected '" + exp + "' in exception message: "; 241 assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp)); 242 } 243 assertTrue(newGroup.getServers().contains(targetServer.getAddress())); 244 245 // remove dead servers 246 NUM_DEAD_SERVERS = cluster.getClusterMetrics().getDeadServerNames().size(); 247 try { 248 // stopping may cause an exception 249 // due to the connection loss 250 LOG.info("stopping server " + targetServer.getServerName()); 251 admin.stopRegionServer(targetServer.getAddress().toString()); 252 NUM_DEAD_SERVERS++; 253 } catch (Exception e) { 254 } 255 256 // wait for stopped regionserver to dead server list 257 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 258 @Override 259 public boolean evaluate() throws Exception { 260 return !master.getServerManager().areDeadServersInProgress() 261 && cluster.getClusterMetrics().getDeadServerNames().size() == NUM_DEAD_SERVERS; 262 } 263 }); 264 265 try { 266 rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress())); 267 fail("Dead servers shouldn't have been successfully removed."); 268 } catch (IOException ex) { 269 String exp = "Server " + targetServer.getAddress() + " is on the dead servers list," 270 + " Maybe it will come back again, not allowed to remove."; 271 String msg = "Expected '" + exp + "' in exception message: "; 272 assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp)); 273 } 274 assertTrue(newGroup.getServers().contains(targetServer.getAddress())); 275 276 // remove decommissioned servers 277 List<ServerName> serversToDecommission = new ArrayList<>(); 278 targetServer = getServerName(iterator.next()); 279 assertTrue(master.getServerManager().getOnlineServers().containsKey(targetServer)); 280 serversToDecommission.add(targetServer); 281 282 admin.decommissionRegionServers(serversToDecommission, true); 283 assertEquals(1, admin.listDecommissionedRegionServers().size()); 284 285 assertTrue(newGroup.getServers().contains(targetServer.getAddress())); 286 rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress())); 287 Set<Address> newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers(); 288 assertFalse(newGroupServers.contains(targetServer.getAddress())); 289 assertEquals(2, newGroupServers.size()); 290 291 assertTrue(observer.preRemoveServersCalled); 292 assertTrue(observer.postRemoveServersCalled); 293 } 294 295 @Test 296 public void testMoveServersAndTables() throws Exception { 297 LOG.info("testMoveServersAndTables"); 298 final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 1); 299 // create table 300 final byte[] familyNameBytes = Bytes.toBytes("f"); 301 TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5); 302 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 303 @Override 304 public boolean evaluate() throws Exception { 305 List<String> regions = getTableRegionMap().get(tableName); 306 if (regions == null) { 307 return false; 308 } 309 310 return getTableRegionMap().get(tableName).size() >= 5; 311 } 312 }); 313 314 // get server which is not a member of new group 315 ServerName targetServer = null; 316 for (ServerName server : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)) 317 .getLiveServerMetrics().keySet()) { 318 if ( 319 !newGroup.containsServer(server.getAddress()) 320 && !rsGroupAdmin.getRSGroupInfo("master").containsServer(server.getAddress()) 321 ) { 322 targetServer = server; 323 break; 324 } 325 } 326 327 LOG.debug("Print group info : " + rsGroupAdmin.listRSGroups()); 328 int oldDefaultGroupServerSize = 329 rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size(); 330 int oldDefaultGroupTableSize = 331 rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables().size(); 332 333 // test fail bogus server move 334 try { 335 rsGroupAdmin.moveServersAndTables(Sets.newHashSet(Address.fromString("foo:9999")), 336 Sets.newHashSet(tableName), newGroup.getName()); 337 fail("Bogus servers shouldn't have been successfully moved."); 338 } catch (IOException ex) { 339 String exp = "Server foo:9999 is either offline or it does not exist."; 340 String msg = "Expected '" + exp + "' in exception message: "; 341 assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp)); 342 } 343 344 // test move when src = dst 345 rsGroupAdmin.moveServersAndTables(Sets.newHashSet(targetServer.getAddress()), 346 Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP); 347 348 // verify default group info 349 Assert.assertEquals(oldDefaultGroupServerSize, 350 rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size()); 351 Assert.assertEquals(oldDefaultGroupTableSize, 352 rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables().size()); 353 354 // verify new group info 355 Assert.assertEquals(1, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers().size()); 356 Assert.assertEquals(0, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size()); 357 358 // get all region to move targetServer 359 List<String> regionList = getTableRegionMap().get(tableName); 360 for (String region : regionList) { 361 // Lets move this region to the targetServer 362 TEST_UTIL.getAdmin().move(Bytes.toBytes(RegionInfo.encodeRegionName(Bytes.toBytes(region))), 363 targetServer); 364 } 365 366 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 367 @Override 368 public boolean evaluate() throws Exception { 369 return getTableRegionMap().get(tableName) != null 370 && getTableRegionMap().get(tableName).size() == 5 371 && getTableServerRegionMap().get(tableName).size() == 1 372 && admin.getClusterMetrics(EnumSet.of(Option.REGIONS_IN_TRANSITION)) 373 .getRegionStatesInTransition().size() < 1; 374 } 375 }); 376 377 // verify that all region move to targetServer 378 Assert.assertEquals(5, getTableServerRegionMap().get(tableName).get(targetServer).size()); 379 380 // move targetServer and table to newGroup 381 LOG.info("moving server and table to newGroup"); 382 rsGroupAdmin.moveServersAndTables(Sets.newHashSet(targetServer.getAddress()), 383 Sets.newHashSet(tableName), newGroup.getName()); 384 385 // verify group change 386 Assert.assertEquals(newGroup.getName(), 387 rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName()); 388 389 // verify servers' not exist in old group 390 Set<Address> defaultServers = 391 rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers(); 392 assertFalse(defaultServers.contains(targetServer.getAddress())); 393 394 // verify servers' exist in new group 395 Set<Address> newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers(); 396 assertTrue(newGroupServers.contains(targetServer.getAddress())); 397 398 // verify tables' not exist in old group 399 Set<TableName> defaultTables = 400 rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables(); 401 assertFalse(defaultTables.contains(tableName)); 402 403 // verify tables' exist in new group 404 Set<TableName> newGroupTables = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables(); 405 assertTrue(newGroupTables.contains(tableName)); 406 407 // verify that all region still assgin on targetServer 408 Assert.assertEquals(5, getTableServerRegionMap().get(tableName).get(targetServer).size()); 409 410 assertTrue(observer.preMoveServersAndTables); 411 assertTrue(observer.postMoveServersAndTables); 412 } 413 414 @Test 415 public void testMoveServersFromDefaultGroup() throws Exception { 416 // create groups and assign servers 417 rsGroupAdmin.addRSGroup("foo"); 418 419 RSGroupInfo fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); 420 assertEquals(0, fooGroup.getServers().size()); 421 RSGroupInfo defaultGroup = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP); 422 423 // test remove all servers from default 424 try { 425 rsGroupAdmin.moveServers(defaultGroup.getServers(), fooGroup.getName()); 426 fail(RSGroupAdminServer.KEEP_ONE_SERVER_IN_DEFAULT_ERROR_MESSAGE); 427 } catch (ConstraintException ex) { 428 assertTrue( 429 ex.getMessage().contains(RSGroupAdminServer.KEEP_ONE_SERVER_IN_DEFAULT_ERROR_MESSAGE)); 430 } 431 432 // test success case, remove one server from default ,keep at least one server 433 if (defaultGroup.getServers().size() > 1) { 434 Address serverInDefaultGroup = defaultGroup.getServers().iterator().next(); 435 LOG.info("moving server " + serverInDefaultGroup + " from group default to group " 436 + fooGroup.getName()); 437 rsGroupAdmin.moveServers(Sets.newHashSet(serverInDefaultGroup), fooGroup.getName()); 438 } 439 440 fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); 441 LOG.info("moving servers " + fooGroup.getServers() + " to group default"); 442 rsGroupAdmin.moveServers(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP); 443 444 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 445 @Override 446 public boolean evaluate() throws Exception { 447 return getNumServers() 448 == rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size(); 449 } 450 }); 451 452 fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); 453 assertEquals(0, fooGroup.getServers().size()); 454 455 // test group removal 456 LOG.info("Remove group " + fooGroup.getName()); 457 rsGroupAdmin.removeRSGroup(fooGroup.getName()); 458 Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(fooGroup.getName())); 459 } 460 461 @Test 462 public void testFailedMoveWhenMoveServer() throws Exception { 463 final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 1); 464 final byte[] familyNameBytes = Bytes.toBytes("f"); 465 final int tableRegionCount = 10; 466 // All the regions created below will be assigned to the default group. 467 TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, tableRegionCount); 468 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 469 @Override 470 public boolean evaluate() throws Exception { 471 List<String> regions = getTableRegionMap().get(tableName); 472 if (regions == null) { 473 return false; 474 } 475 return getTableRegionMap().get(tableName).size() >= tableRegionCount; 476 } 477 }); 478 479 // get target server to move, which should has more than one regions 480 // randomly set a region state to SPLITTING 481 Map<ServerName, List<String>> assignMap = getTableServerRegionMap().get(tableName); 482 String rregion = null; 483 ServerName toMoveServer = null; 484 for (ServerName server : assignMap.keySet()) { 485 rregion = assignMap.get(server).size() > 1 && !newGroup.containsServer(server.getAddress()) 486 ? assignMap.get(server).get(0) 487 : null; 488 if (rregion != null) { 489 toMoveServer = server; 490 break; 491 } 492 } 493 assert toMoveServer != null; 494 RegionInfo ri = TEST_UTIL.getMiniHBaseCluster().getMaster().getAssignmentManager() 495 .getRegionInfo(Bytes.toBytesBinary(rregion)); 496 RegionStateNode rsn = TEST_UTIL.getMiniHBaseCluster().getMaster().getAssignmentManager() 497 .getRegionStates().getRegionStateNode(ri); 498 rsn.setState(RegionState.State.SPLITTING); 499 500 // start thread to recover region state 501 final ServerName movedServer = toMoveServer; 502 final String sregion = rregion; 503 AtomicBoolean changed = new AtomicBoolean(false); 504 Thread t1 = new Thread(() -> { 505 LOG.debug("thread1 start running, will recover region state"); 506 long current = EnvironmentEdgeManager.currentTime(); 507 while (EnvironmentEdgeManager.currentTime() - current <= 50000) { 508 List<RegionInfo> regions = master.getAssignmentManager().getRegionsOnServer(movedServer); 509 LOG.debug("server region size is:{}", regions.size()); 510 assert regions.size() >= 1; 511 // when there is exactly one region left, we can determine the move operation encountered 512 // exception caused by the strange region state. 513 if (regions.size() == 1) { 514 assertEquals(regions.get(0).getRegionNameAsString(), sregion); 515 rsn.setState(RegionState.State.OPEN); 516 LOG.info("set region {} state OPEN", sregion); 517 changed.set(true); 518 break; 519 } 520 sleep(5000); 521 } 522 }); 523 t1.start(); 524 525 // move target server to group 526 Thread t2 = new Thread(() -> { 527 LOG.info("thread2 start running, to move regions"); 528 try { 529 rsGroupAdmin.moveServers(Sets.newHashSet(movedServer.getAddress()), newGroup.getName()); 530 } catch (IOException e) { 531 LOG.error("move server error", e); 532 } 533 }); 534 t2.start(); 535 536 t1.join(); 537 t2.join(); 538 539 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 540 @Override 541 public boolean evaluate() { 542 if (changed.get()) { 543 return master.getAssignmentManager().getRegionsOnServer(movedServer).size() == 0 544 && !rsn.getRegionLocation().equals(movedServer); 545 } 546 return false; 547 } 548 }); 549 } 550 551 @Test 552 public void testFailedMoveWhenMoveTable() throws Exception { 553 final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 1); 554 final byte[] familyNameBytes = Bytes.toBytes("f"); 555 final int tableRegionCount = 5; 556 // All the regions created below will be assigned to the default group. 557 TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, tableRegionCount); 558 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 559 @Override 560 public boolean evaluate() throws Exception { 561 List<String> regions = getTableRegionMap().get(tableName); 562 if (regions == null) { 563 return false; 564 } 565 return getTableRegionMap().get(tableName).size() >= tableRegionCount; 566 } 567 }); 568 569 // randomly set a region state to SPLITTING 570 Map<ServerName, List<String>> assignMap = getTableServerRegionMap().get(tableName); 571 String rregion = null; 572 ServerName srcServer = null; 573 for (ServerName server : assignMap.keySet()) { 574 rregion = assignMap.get(server).size() >= 1 && !newGroup.containsServer(server.getAddress()) 575 ? assignMap.get(server).get(0) 576 : null; 577 if (rregion != null) { 578 srcServer = server; 579 break; 580 } 581 } 582 assert srcServer != null; 583 RegionInfo ri = TEST_UTIL.getMiniHBaseCluster().getMaster().getAssignmentManager() 584 .getRegionInfo(Bytes.toBytesBinary(rregion)); 585 RegionStateNode rsn = TEST_UTIL.getMiniHBaseCluster().getMaster().getAssignmentManager() 586 .getRegionStates().getRegionStateNode(ri); 587 rsn.setState(RegionState.State.SPLITTING); 588 589 // move table to group 590 Thread t2 = new Thread(() -> { 591 LOG.info("thread2 start running, to move regions"); 592 try { 593 rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName()); 594 } catch (IOException e) { 595 LOG.error("move server error", e); 596 } 597 }); 598 t2.start(); 599 600 // start thread to recover region state 601 final ServerName ss = srcServer; 602 final String sregion = rregion; 603 AtomicBoolean changed = new AtomicBoolean(false); 604 Thread t1 = new Thread(() -> { 605 LOG.info("thread1 start running, will recover region state"); 606 long current = EnvironmentEdgeManager.currentTime(); 607 while (EnvironmentEdgeManager.currentTime() - current <= 50000) { 608 List<RegionInfo> regions = master.getAssignmentManager().getRegionsOnServer(ss); 609 List<RegionInfo> tableRegions = new ArrayList<>(); 610 for (RegionInfo regionInfo : regions) { 611 if (regionInfo.getTable().equals(tableName)) { 612 tableRegions.add(regionInfo); 613 } 614 } 615 LOG.debug("server table region size is:{}", tableRegions.size()); 616 assert tableRegions.size() >= 1; 617 // when there is exactly one region left, we can determine the move operation encountered 618 // exception caused by the strange region state. 619 if (tableRegions.size() == 1) { 620 assertEquals(tableRegions.get(0).getRegionNameAsString(), sregion); 621 rsn.setState(RegionState.State.OPEN); 622 LOG.info("set region {} state OPEN", sregion); 623 changed.set(true); 624 break; 625 } 626 sleep(5000); 627 } 628 }); 629 t1.start(); 630 631 t1.join(); 632 t2.join(); 633 634 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 635 @Override 636 public boolean evaluate() { 637 if (changed.get()) { 638 boolean serverHasTableRegions = false; 639 for (RegionInfo regionInfo : master.getAssignmentManager().getRegionsOnServer(ss)) { 640 if (regionInfo.getTable().equals(tableName)) { 641 serverHasTableRegions = true; 642 break; 643 } 644 } 645 return !serverHasTableRegions && !rsn.getRegionLocation().equals(ss); 646 } 647 return false; 648 } 649 }); 650 } 651 652 @Test 653 public void testMoveTablePerformance() throws Exception { 654 final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 1); 655 final byte[] familyNameBytes = Bytes.toBytes("f"); 656 final int tableRegionCount = 100; 657 // All the regions created below will be assigned to the default group. 658 TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, tableRegionCount); 659 TEST_UTIL.waitFor(WAIT_TIMEOUT, (Waiter.Predicate<Exception>) () -> { 660 List<String> regions = getTableRegionMap().get(tableName); 661 if (regions == null) { 662 return false; 663 } 664 return getTableRegionMap().get(tableName).size() >= tableRegionCount; 665 }); 666 long startTime = EnvironmentEdgeManager.currentTime(); 667 rsGroupAdmin.moveServers(Sets.newHashSet(newGroup.getServers().iterator().next()), 668 newGroup.getName()); 669 long timeTaken = EnvironmentEdgeManager.currentTime() - startTime; 670 String msg = 671 "Should not take mote than 15000 ms to move a table with 100 regions. Time taken =" 672 + timeTaken + " ms"; 673 // This test case is meant to be used for verifying the performance quickly by a developer. 674 // Moving 100 regions takes much less than 15000 ms. Given 15000 ms so test cases passes 675 // on all environment. 676 assertTrue(msg, timeTaken < 15000); 677 LOG.info("Time taken to move a table with 100 region is {} ms", timeTaken); 678 } 679}