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.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertNull; 023import static org.junit.Assert.assertTrue; 024import static org.junit.Assert.fail; 025 026import java.io.IOException; 027import java.security.SecureRandom; 028import java.util.ArrayList; 029import java.util.EnumSet; 030import java.util.HashSet; 031import java.util.Iterator; 032import java.util.LinkedList; 033import java.util.List; 034import java.util.Map; 035import java.util.Set; 036import java.util.TreeMap; 037import org.apache.hadoop.hbase.ClusterMetrics; 038import org.apache.hadoop.hbase.ClusterMetrics.Option; 039import org.apache.hadoop.hbase.HBaseCluster; 040import org.apache.hadoop.hbase.HBaseTestingUtility; 041import org.apache.hadoop.hbase.HColumnDescriptor; 042import org.apache.hadoop.hbase.HTableDescriptor; 043import org.apache.hadoop.hbase.NamespaceDescriptor; 044import org.apache.hadoop.hbase.RegionMetrics; 045import org.apache.hadoop.hbase.ServerMetrics; 046import org.apache.hadoop.hbase.ServerName; 047import org.apache.hadoop.hbase.TableName; 048import org.apache.hadoop.hbase.Waiter; 049import org.apache.hadoop.hbase.client.Admin; 050import org.apache.hadoop.hbase.client.ClusterConnection; 051import org.apache.hadoop.hbase.client.RegionInfo; 052import org.apache.hadoop.hbase.constraint.ConstraintException; 053import org.apache.hadoop.hbase.master.HMaster; 054import org.apache.hadoop.hbase.net.Address; 055import org.apache.hadoop.hbase.util.Bytes; 056import org.junit.Assert; 057import org.junit.Before; 058import org.junit.Rule; 059import org.junit.Test; 060import org.junit.rules.TestName; 061import org.slf4j.Logger; 062import org.slf4j.LoggerFactory; 063 064import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 065import org.apache.hbase.thirdparty.com.google.common.collect.Maps; 066import org.apache.hbase.thirdparty.com.google.common.collect.Sets; 067import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 068import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos; 069import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.GetServerInfoRequest; 070 071public abstract class TestRSGroupsBase { 072 protected static final Logger LOG = LoggerFactory.getLogger(TestRSGroupsBase.class); 073 @Rule 074 public TestName name = new TestName(); 075 076 //shared 077 protected final static String groupPrefix = "Group"; 078 protected final static String tablePrefix = "Group"; 079 protected final static SecureRandom rand = new SecureRandom(); 080 081 //shared, cluster type specific 082 protected static HBaseTestingUtility TEST_UTIL; 083 protected static Admin admin; 084 protected static HBaseCluster cluster; 085 protected static RSGroupAdmin rsGroupAdmin; 086 protected static HMaster master; 087 088 public final static long WAIT_TIMEOUT = 60000*5; 089 public final static int NUM_SLAVES_BASE = 4; //number of slaves for the smallest cluster 090 public static int NUM_DEAD_SERVERS = 0; 091 092 // Per test variables 093 TableName tableName; 094 @Before 095 public void setup() { 096 LOG.info(name.getMethodName()); 097 tableName = TableName.valueOf(tablePrefix + "_" + name.getMethodName()); 098 } 099 100 protected RSGroupInfo addGroup(String groupName, int serverCount) 101 throws IOException, InterruptedException { 102 RSGroupInfo defaultInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP); 103 assertTrue(defaultInfo != null); 104 assertTrue(defaultInfo.getServers().size() >= serverCount); 105 rsGroupAdmin.addRSGroup(groupName); 106 107 Set<Address> set = new HashSet<>(); 108 for(Address server: defaultInfo.getServers()) { 109 if(set.size() == serverCount) { 110 break; 111 } 112 set.add(server); 113 } 114 rsGroupAdmin.moveServers(set, groupName); 115 RSGroupInfo result = rsGroupAdmin.getRSGroupInfo(groupName); 116 assertTrue(result.getServers().size() >= serverCount); 117 return result; 118 } 119 120 void removeGroup(String groupName) throws IOException { 121 RSGroupInfo RSGroupInfo = rsGroupAdmin.getRSGroupInfo(groupName); 122 rsGroupAdmin.moveTables(RSGroupInfo.getTables(), RSGroupInfo.DEFAULT_GROUP); 123 rsGroupAdmin.moveServers(RSGroupInfo.getServers(), RSGroupInfo.DEFAULT_GROUP); 124 rsGroupAdmin.removeRSGroup(groupName); 125 } 126 127 protected void deleteTableIfNecessary() throws IOException { 128 for (HTableDescriptor desc : TEST_UTIL.getAdmin().listTables(tablePrefix+".*")) { 129 TEST_UTIL.deleteTable(desc.getTableName()); 130 } 131 } 132 133 protected void deleteNamespaceIfNecessary() throws IOException { 134 for (NamespaceDescriptor desc : TEST_UTIL.getAdmin().listNamespaceDescriptors()) { 135 if(desc.getName().startsWith(tablePrefix)) { 136 admin.deleteNamespace(desc.getName()); 137 } 138 } 139 } 140 141 protected void deleteGroups() throws IOException { 142 RSGroupAdmin groupAdmin = 143 new RSGroupAdminClient(TEST_UTIL.getConnection()); 144 for(RSGroupInfo group: groupAdmin.listRSGroups()) { 145 if(!group.getName().equals(RSGroupInfo.DEFAULT_GROUP)) { 146 groupAdmin.moveTables(group.getTables(), RSGroupInfo.DEFAULT_GROUP); 147 groupAdmin.moveServers(group.getServers(), RSGroupInfo.DEFAULT_GROUP); 148 groupAdmin.removeRSGroup(group.getName()); 149 } 150 } 151 } 152 153 public Map<TableName, List<String>> getTableRegionMap() throws IOException { 154 Map<TableName, List<String>> map = Maps.newTreeMap(); 155 Map<TableName, Map<ServerName, List<String>>> tableServerRegionMap 156 = getTableServerRegionMap(); 157 for(TableName tableName : tableServerRegionMap.keySet()) { 158 if(!map.containsKey(tableName)) { 159 map.put(tableName, new LinkedList<>()); 160 } 161 for(List<String> subset: tableServerRegionMap.get(tableName).values()) { 162 map.get(tableName).addAll(subset); 163 } 164 } 165 return map; 166 } 167 168 public Map<TableName, Map<ServerName, List<String>>> getTableServerRegionMap() 169 throws IOException { 170 Map<TableName, Map<ServerName, List<String>>> map = Maps.newTreeMap(); 171 ClusterMetrics status = TEST_UTIL.getHBaseClusterInterface().getClusterMetrics(); 172 for (Map.Entry<ServerName, ServerMetrics> entry : status.getLiveServerMetrics().entrySet()) { 173 ServerName serverName = entry.getKey(); 174 for(RegionMetrics rl : entry.getValue().getRegionMetrics().values()) { 175 TableName tableName = null; 176 try { 177 tableName = RegionInfo.getTable(rl.getRegionName()); 178 } catch (IllegalArgumentException e) { 179 LOG.warn("Failed parse a table name from regionname=" + 180 Bytes.toStringBinary(rl.getRegionName())); 181 continue; 182 } 183 if(!map.containsKey(tableName)) { 184 map.put(tableName, new TreeMap<>()); 185 } 186 if(!map.get(tableName).containsKey(serverName)) { 187 map.get(tableName).put(serverName, new LinkedList<>()); 188 } 189 map.get(tableName).get(serverName).add(rl.getNameAsString()); 190 } 191 } 192 return map; 193 } 194 195 @Test 196 public void testBogusArgs() throws Exception { 197 assertNull(rsGroupAdmin.getRSGroupInfoOfTable(TableName.valueOf("nonexistent"))); 198 assertNull(rsGroupAdmin.getRSGroupOfServer(Address.fromParts("bogus",123))); 199 assertNull(rsGroupAdmin.getRSGroupInfo("bogus")); 200 201 try { 202 rsGroupAdmin.removeRSGroup("bogus"); 203 fail("Expected removing bogus group to fail"); 204 } catch(ConstraintException ex) { 205 //expected 206 } 207 208 try { 209 rsGroupAdmin.moveTables(Sets.newHashSet(TableName.valueOf("bogustable")), "bogus"); 210 fail("Expected move with bogus group to fail"); 211 } catch(ConstraintException ex) { 212 //expected 213 } 214 215 try { 216 rsGroupAdmin.moveServers(Sets.newHashSet(Address.fromParts("bogus",123)), "bogus"); 217 fail("Expected move with bogus group to fail"); 218 } catch(ConstraintException ex) { 219 //expected 220 } 221 222 try { 223 admin.setBalancerRunning(true,true); 224 rsGroupAdmin.balanceRSGroup("bogus"); 225 admin.setBalancerRunning(false,true); 226 fail("Expected move with bogus group to fail"); 227 } catch(ConstraintException ex) { 228 //expected 229 } 230 } 231 232 @Test 233 public void testCreateMultiRegion() throws IOException { 234 byte[] end = {1,3,5,7,9}; 235 byte[] start = {0,2,4,6,8}; 236 byte[][] f = {Bytes.toBytes("f")}; 237 TEST_UTIL.createTable(tableName, f,1,start,end,10); 238 } 239 240 @Test 241 public void testCreateAndDrop() throws Exception { 242 TEST_UTIL.createTable(tableName, Bytes.toBytes("cf")); 243 //wait for created table to be assigned 244 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 245 @Override 246 public boolean evaluate() throws Exception { 247 return getTableRegionMap().get(tableName) != null; 248 } 249 }); 250 TEST_UTIL.deleteTable(tableName); 251 } 252 253 254 @Test 255 public void testSimpleRegionServerMove() throws IOException, 256 InterruptedException { 257 int initNumGroups = rsGroupAdmin.listRSGroups().size(); 258 RSGroupInfo appInfo = addGroup(getGroupName(name.getMethodName()), 1); 259 RSGroupInfo adminInfo = addGroup(getGroupName(name.getMethodName()), 1); 260 RSGroupInfo dInfo = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP); 261 Assert.assertEquals(initNumGroups + 2, rsGroupAdmin.listRSGroups().size()); 262 assertEquals(1, adminInfo.getServers().size()); 263 assertEquals(1, appInfo.getServers().size()); 264 assertEquals(getNumServers() - 2, dInfo.getServers().size()); 265 rsGroupAdmin.moveServers(appInfo.getServers(), 266 RSGroupInfo.DEFAULT_GROUP); 267 rsGroupAdmin.removeRSGroup(appInfo.getName()); 268 rsGroupAdmin.moveServers(adminInfo.getServers(), 269 RSGroupInfo.DEFAULT_GROUP); 270 rsGroupAdmin.removeRSGroup(adminInfo.getName()); 271 Assert.assertEquals(rsGroupAdmin.listRSGroups().size(), initNumGroups); 272 } 273 274 // return the real number of region servers, excluding the master embedded region server in 2.0+ 275 public int getNumServers() throws IOException { 276 ClusterMetrics status = 277 admin.getClusterMetrics(EnumSet.of(Option.MASTER, Option.LIVE_SERVERS)); 278 ServerName masterName = status.getMasterName(); 279 int count = 0; 280 for (ServerName sn : status.getLiveServerMetrics().keySet()) { 281 if (!sn.equals(masterName)) { 282 count++; 283 } 284 } 285 return count; 286 } 287 288 @Test 289 public void testMoveServers() throws Exception { 290 //create groups and assign servers 291 addGroup("bar", 3); 292 rsGroupAdmin.addRSGroup("foo"); 293 294 RSGroupInfo barGroup = rsGroupAdmin.getRSGroupInfo("bar"); 295 RSGroupInfo fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); 296 assertEquals(3, barGroup.getServers().size()); 297 assertEquals(0, fooGroup.getServers().size()); 298 299 //test fail bogus server move 300 try { 301 rsGroupAdmin.moveServers(Sets.newHashSet(Address.fromString("foo:9999")),"foo"); 302 fail("Bogus servers shouldn't have been successfully moved."); 303 } catch(IOException ex) { 304 String exp = "Source RSGroup for server foo:9999 does not exist."; 305 String msg = "Expected '"+exp+"' in exception message: "; 306 assertTrue(msg+" "+ex.getMessage(), ex.getMessage().contains(exp)); 307 } 308 309 //test success case 310 LOG.info("moving servers "+barGroup.getServers()+" to group foo"); 311 rsGroupAdmin.moveServers(barGroup.getServers(), fooGroup.getName()); 312 313 barGroup = rsGroupAdmin.getRSGroupInfo("bar"); 314 fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); 315 assertEquals(0,barGroup.getServers().size()); 316 assertEquals(3,fooGroup.getServers().size()); 317 318 LOG.info("moving servers "+fooGroup.getServers()+" to group default"); 319 rsGroupAdmin.moveServers(fooGroup.getServers(), RSGroupInfo.DEFAULT_GROUP); 320 321 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 322 @Override 323 public boolean evaluate() throws Exception { 324 return getNumServers() == 325 rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size(); 326 } 327 }); 328 329 fooGroup = rsGroupAdmin.getRSGroupInfo("foo"); 330 assertEquals(0,fooGroup.getServers().size()); 331 332 //test group removal 333 LOG.info("Remove group "+barGroup.getName()); 334 rsGroupAdmin.removeRSGroup(barGroup.getName()); 335 Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(barGroup.getName())); 336 LOG.info("Remove group "+fooGroup.getName()); 337 rsGroupAdmin.removeRSGroup(fooGroup.getName()); 338 Assert.assertEquals(null, rsGroupAdmin.getRSGroupInfo(fooGroup.getName())); 339 } 340 341 @Test 342 public void testTableMoveTruncateAndDrop() throws Exception { 343 final byte[] familyNameBytes = Bytes.toBytes("f"); 344 String newGroupName = getGroupName(name.getMethodName()); 345 final RSGroupInfo newGroup = addGroup(newGroupName, 2); 346 347 TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5); 348 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 349 @Override 350 public boolean evaluate() throws Exception { 351 List<String> regions = getTableRegionMap().get(tableName); 352 if (regions == null) { 353 return false; 354 } 355 356 return getTableRegionMap().get(tableName).size() >= 5; 357 } 358 }); 359 360 RSGroupInfo tableGrp = rsGroupAdmin.getRSGroupInfoOfTable(tableName); 361 assertTrue(tableGrp.getName().equals(RSGroupInfo.DEFAULT_GROUP)); 362 363 //change table's group 364 LOG.info("Moving table "+tableName+" to "+newGroup.getName()); 365 rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName()); 366 367 //verify group change 368 Assert.assertEquals(newGroup.getName(), 369 rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName()); 370 371 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 372 @Override 373 public boolean evaluate() throws Exception { 374 Map<ServerName, List<String>> serverMap = getTableServerRegionMap().get(tableName); 375 int count = 0; 376 if (serverMap != null) { 377 for (ServerName rs : serverMap.keySet()) { 378 if (newGroup.containsServer(rs.getAddress())) { 379 count += serverMap.get(rs).size(); 380 } 381 } 382 } 383 return count == 5; 384 } 385 }); 386 387 //test truncate 388 admin.disableTable(tableName); 389 admin.truncateTable(tableName, true); 390 Assert.assertEquals(1, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size()); 391 Assert.assertEquals(tableName, rsGroupAdmin.getRSGroupInfo( 392 newGroup.getName()).getTables().first()); 393 394 //verify removed table is removed from group 395 TEST_UTIL.deleteTable(tableName); 396 Assert.assertEquals(0, rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size()); 397 } 398 399 @Test 400 public void testGroupBalance() throws Exception { 401 LOG.info(name.getMethodName()); 402 String newGroupName = getGroupName(name.getMethodName()); 403 final RSGroupInfo newGroup = addGroup(newGroupName, 3); 404 405 final TableName tableName = TableName.valueOf(tablePrefix+"_ns", name.getMethodName()); 406 admin.createNamespace( 407 NamespaceDescriptor.create(tableName.getNamespaceAsString()) 408 .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, newGroupName).build()); 409 final byte[] familyNameBytes = Bytes.toBytes("f"); 410 final HTableDescriptor desc = new HTableDescriptor(tableName); 411 desc.addFamily(new HColumnDescriptor("f")); 412 byte [] startKey = Bytes.toBytes("aaaaa"); 413 byte [] endKey = Bytes.toBytes("zzzzz"); 414 admin.createTable(desc, startKey, endKey, 6); 415 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 416 @Override 417 public boolean evaluate() throws Exception { 418 List<String> regions = getTableRegionMap().get(tableName); 419 if (regions == null) { 420 return false; 421 } 422 return regions.size() >= 6; 423 } 424 }); 425 426 //make assignment uneven, move all regions to one server 427 Map<ServerName,List<String>> assignMap = 428 getTableServerRegionMap().get(tableName); 429 final ServerName first = assignMap.entrySet().iterator().next().getKey(); 430 for(RegionInfo region: admin.getTableRegions(tableName)) { 431 if(!assignMap.get(first).contains(region.getRegionNameAsString())) { 432 admin.move(region.getEncodedNameAsBytes(), Bytes.toBytes(first.getServerName())); 433 } 434 } 435 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 436 @Override 437 public boolean evaluate() throws Exception { 438 Map<ServerName, List<String>> map = getTableServerRegionMap().get(tableName); 439 if (map == null) { 440 return true; 441 } 442 List<String> regions = map.get(first); 443 if (regions == null) { 444 return true; 445 } 446 return regions.size() >= 6; 447 } 448 }); 449 450 //balance the other group and make sure it doesn't affect the new group 451 admin.setBalancerRunning(true,true); 452 rsGroupAdmin.balanceRSGroup(RSGroupInfo.DEFAULT_GROUP); 453 assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size()); 454 455 //disable balance, balancer will not be run and return false 456 admin.setBalancerRunning(false,true); 457 assertFalse(rsGroupAdmin.balanceRSGroup(newGroupName)); 458 assertEquals(6, getTableServerRegionMap().get(tableName).get(first).size()); 459 460 //enable balance 461 admin.setBalancerRunning(true,true); 462 rsGroupAdmin.balanceRSGroup(newGroupName); 463 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 464 @Override 465 public boolean evaluate() throws Exception { 466 for (List<String> regions : getTableServerRegionMap().get(tableName).values()) { 467 if (2 != regions.size()) { 468 return false; 469 } 470 } 471 return true; 472 } 473 }); 474 admin.setBalancerRunning(false,true); 475 } 476 477 @Test 478 public void testRegionMove() throws Exception { 479 final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 1); 480 final byte[] familyNameBytes = Bytes.toBytes("f"); 481 // All the regions created below will be assigned to the default group. 482 TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 6); 483 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 484 @Override 485 public boolean evaluate() throws Exception { 486 List<String> regions = getTableRegionMap().get(tableName); 487 if (regions == null) { 488 return false; 489 } 490 return getTableRegionMap().get(tableName).size() >= 6; 491 } 492 }); 493 494 //get target region to move 495 Map<ServerName,List<String>> assignMap = 496 getTableServerRegionMap().get(tableName); 497 String targetRegion = null; 498 for(ServerName server : assignMap.keySet()) { 499 targetRegion = assignMap.get(server).size() > 0 ? assignMap.get(server).get(0) : null; 500 if(targetRegion != null) { 501 break; 502 } 503 } 504 //get server which is not a member of new group 505 ServerName targetServer = null; 506 for (ServerName server : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)) 507 .getLiveServerMetrics().keySet()) { 508 if (!newGroup.containsServer(server.getAddress())) { 509 targetServer = server; 510 break; 511 } 512 } 513 514 final AdminProtos.AdminService.BlockingInterface targetRS = 515 ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); 516 517 //move target server to group 518 rsGroupAdmin.moveServers(Sets.newHashSet(targetServer.getAddress()), 519 newGroup.getName()); 520 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 521 @Override 522 public boolean evaluate() throws Exception { 523 return ProtobufUtil.getOnlineRegions(targetRS).size() <= 0; 524 } 525 }); 526 527 // Lets move this region to the new group. 528 TEST_UTIL.getAdmin().move(Bytes.toBytes(RegionInfo.encodeRegionName( 529 Bytes.toBytes(targetRegion))), Bytes.toBytes(targetServer.getServerName())); 530 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 531 @Override 532 public boolean evaluate() throws Exception { 533 return 534 getTableRegionMap().get(tableName) != null && 535 getTableRegionMap().get(tableName).size() == 6 && 536 admin.getClusterMetrics(EnumSet.of(Option.REGIONS_IN_TRANSITION)) 537 .getRegionStatesInTransition().size() < 1; 538 } 539 }); 540 541 //verify that targetServer didn't open it 542 for (RegionInfo region: ProtobufUtil.getOnlineRegions(targetRS)) { 543 if (targetRegion.equals(region.getRegionNameAsString())) { 544 fail("Target server opened region"); 545 } 546 } 547 } 548 549 @Test 550 public void testFailRemoveGroup() throws IOException, InterruptedException { 551 int initNumGroups = rsGroupAdmin.listRSGroups().size(); 552 addGroup("bar", 3); 553 TEST_UTIL.createTable(tableName, Bytes.toBytes("f")); 554 rsGroupAdmin.moveTables(Sets.newHashSet(tableName), "bar"); 555 RSGroupInfo barGroup = rsGroupAdmin.getRSGroupInfo("bar"); 556 //group is not empty therefore it should fail 557 try { 558 rsGroupAdmin.removeRSGroup(barGroup.getName()); 559 fail("Expected remove group to fail"); 560 } catch(IOException e) { 561 } 562 //group cannot lose all it's servers therefore it should fail 563 try { 564 rsGroupAdmin.moveServers(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP); 565 fail("Expected move servers to fail"); 566 } catch(IOException e) { 567 } 568 569 rsGroupAdmin.moveTables(barGroup.getTables(), RSGroupInfo.DEFAULT_GROUP); 570 try { 571 rsGroupAdmin.removeRSGroup(barGroup.getName()); 572 fail("Expected move servers to fail"); 573 } catch(IOException e) { 574 } 575 576 rsGroupAdmin.moveServers(barGroup.getServers(), RSGroupInfo.DEFAULT_GROUP); 577 rsGroupAdmin.removeRSGroup(barGroup.getName()); 578 579 Assert.assertEquals(initNumGroups, rsGroupAdmin.listRSGroups().size()); 580 } 581 582 @Test 583 public void testKillRS() throws Exception { 584 RSGroupInfo appInfo = addGroup("appInfo", 1); 585 586 final TableName tableName = TableName.valueOf(tablePrefix+"_ns", name.getMethodName()); 587 admin.createNamespace( 588 NamespaceDescriptor.create(tableName.getNamespaceAsString()) 589 .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, appInfo.getName()).build()); 590 final HTableDescriptor desc = new HTableDescriptor(tableName); 591 desc.addFamily(new HColumnDescriptor("f")); 592 admin.createTable(desc); 593 //wait for created table to be assigned 594 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 595 @Override 596 public boolean evaluate() throws Exception { 597 return getTableRegionMap().get(desc.getTableName()) != null; 598 } 599 }); 600 601 ServerName targetServer = ServerName.parseServerName( 602 appInfo.getServers().iterator().next().toString()); 603 AdminProtos.AdminService.BlockingInterface targetRS = 604 ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); 605 RegionInfo targetRegion = ProtobufUtil.getOnlineRegions(targetRS).get(0); 606 Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(targetRS).size()); 607 608 try { 609 //stopping may cause an exception 610 //due to the connection loss 611 targetRS.stopServer(null, 612 AdminProtos.StopServerRequest.newBuilder().setReason("Die").build()); 613 } catch(Exception e) { 614 } 615 assertFalse(cluster.getClusterMetrics().getLiveServerMetrics().containsKey(targetServer)); 616 617 //wait for created table to be assigned 618 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 619 @Override 620 public boolean evaluate() throws Exception { 621 return cluster.getClusterMetrics().getRegionStatesInTransition().isEmpty(); 622 } 623 }); 624 Set<Address> newServers = Sets.newHashSet(); 625 newServers.add( 626 rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().iterator().next()); 627 rsGroupAdmin.moveServers(newServers, appInfo.getName()); 628 629 //Make sure all the table's regions get reassigned 630 //disabling the table guarantees no conflicting assign/unassign (ie SSH) happens 631 admin.disableTable(tableName); 632 admin.enableTable(tableName); 633 634 //wait for region to be assigned 635 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 636 @Override 637 public boolean evaluate() throws Exception { 638 return cluster.getClusterMetrics().getRegionStatesInTransition().isEmpty(); 639 } 640 }); 641 642 targetServer = ServerName.parseServerName( 643 newServers.iterator().next().toString()); 644 targetRS = 645 ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); 646 Assert.assertEquals(1, ProtobufUtil.getOnlineRegions(targetRS).size()); 647 Assert.assertEquals(tableName, 648 ProtobufUtil.getOnlineRegions(targetRS).get(0).getTable()); 649 } 650 651 @Test 652 public void testValidGroupNames() throws IOException { 653 String[] badNames = {"foo*","foo@","-"}; 654 String[] goodNames = {"foo_123"}; 655 656 for(String entry: badNames) { 657 try { 658 rsGroupAdmin.addRSGroup(entry); 659 fail("Expected a constraint exception for: "+entry); 660 } catch(ConstraintException ex) { 661 //expected 662 } 663 } 664 665 for(String entry: goodNames) { 666 rsGroupAdmin.addRSGroup(entry); 667 } 668 } 669 670 private String getGroupName(String baseName) { 671 return groupPrefix+"_"+baseName+"_"+rand.nextInt(Integer.MAX_VALUE); 672 } 673 674 @Test 675 public void testMultiTableMove() throws Exception { 676 final TableName tableNameA = TableName.valueOf(tablePrefix + name.getMethodName() + "A"); 677 final TableName tableNameB = TableName.valueOf(tablePrefix + name.getMethodName() + "B"); 678 final byte[] familyNameBytes = Bytes.toBytes("f"); 679 String newGroupName = getGroupName(name.getMethodName()); 680 final RSGroupInfo newGroup = addGroup(newGroupName, 1); 681 682 TEST_UTIL.createTable(tableNameA, familyNameBytes); 683 TEST_UTIL.createTable(tableNameB, familyNameBytes); 684 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 685 @Override 686 public boolean evaluate() throws Exception { 687 List<String> regionsA = getTableRegionMap().get(tableNameA); 688 if (regionsA == null) { 689 return false; 690 } 691 692 List<String> regionsB = getTableRegionMap().get(tableNameB); 693 if (regionsB == null) { 694 return false; 695 } 696 697 return getTableRegionMap().get(tableNameA).size() >= 1 698 && getTableRegionMap().get(tableNameB).size() >= 1; 699 } 700 }); 701 702 RSGroupInfo tableGrpA = rsGroupAdmin.getRSGroupInfoOfTable(tableNameA); 703 assertTrue(tableGrpA.getName().equals(RSGroupInfo.DEFAULT_GROUP)); 704 705 RSGroupInfo tableGrpB = rsGroupAdmin.getRSGroupInfoOfTable(tableNameB); 706 assertTrue(tableGrpB.getName().equals(RSGroupInfo.DEFAULT_GROUP)); 707 //change table's group 708 LOG.info("Moving table [" + tableNameA + "," + tableNameB + "] to " + newGroup.getName()); 709 rsGroupAdmin.moveTables(Sets.newHashSet(tableNameA, tableNameB), newGroup.getName()); 710 711 //verify group change 712 Assert.assertEquals(newGroup.getName(), 713 rsGroupAdmin.getRSGroupInfoOfTable(tableNameA).getName()); 714 715 Assert.assertEquals(newGroup.getName(), 716 rsGroupAdmin.getRSGroupInfoOfTable(tableNameB).getName()); 717 718 //verify tables' not exist in old group 719 Set<TableName> DefaultTables = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP) 720 .getTables(); 721 assertFalse(DefaultTables.contains(tableNameA)); 722 assertFalse(DefaultTables.contains(tableNameB)); 723 724 //verify tables' exist in new group 725 Set<TableName> newGroupTables = rsGroupAdmin.getRSGroupInfo(newGroupName).getTables(); 726 assertTrue(newGroupTables.contains(tableNameA)); 727 assertTrue(newGroupTables.contains(tableNameB)); 728 } 729 730 @Test 731 public void testDisabledTableMove() throws Exception { 732 final byte[] familyNameBytes = Bytes.toBytes("f"); 733 String newGroupName = getGroupName(name.getMethodName()); 734 final RSGroupInfo newGroup = addGroup(newGroupName, 2); 735 736 TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5); 737 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 738 @Override 739 public boolean evaluate() throws Exception { 740 List<String> regions = getTableRegionMap().get(tableName); 741 if (regions == null) { 742 return false; 743 } 744 return getTableRegionMap().get(tableName).size() >= 5; 745 } 746 }); 747 748 RSGroupInfo tableGrp = rsGroupAdmin.getRSGroupInfoOfTable(tableName); 749 assertTrue(tableGrp.getName().equals(RSGroupInfo.DEFAULT_GROUP)); 750 751 //test disable table 752 admin.disableTable(tableName); 753 754 //change table's group 755 LOG.info("Moving table "+ tableName + " to " + newGroup.getName()); 756 rsGroupAdmin.moveTables(Sets.newHashSet(tableName), newGroup.getName()); 757 758 //verify group change 759 Assert.assertEquals(newGroup.getName(), 760 rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName()); 761 } 762 763 @Test 764 public void testMoveServersAndTables() throws Exception { 765 LOG.info("testMoveServersAndTables"); 766 final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 1); 767 //create table 768 final byte[] familyNameBytes = Bytes.toBytes("f"); 769 TEST_UTIL.createMultiRegionTable(tableName, familyNameBytes, 5); 770 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 771 @Override 772 public boolean evaluate() throws Exception { 773 List<String> regions = getTableRegionMap().get(tableName); 774 if (regions == null) { 775 return false; 776 } 777 return getTableRegionMap().get(tableName).size() >= 5; 778 } 779 }); 780 781 //get server which is not a member of new group 782 ServerName targetServer = null; 783 for(ServerName server : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)) 784 .getLiveServerMetrics().keySet()) { 785 if(!newGroup.containsServer(server.getAddress()) && 786 !rsGroupAdmin.getRSGroupInfo("master").containsServer(server.getAddress())) { 787 targetServer = server; 788 break; 789 } 790 } 791 792 LOG.debug("Print group info : " + rsGroupAdmin.listRSGroups()); 793 int oldDefaultGroupServerSize = 794 rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size(); 795 int oldDefaultGroupTableSize = 796 rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables().size(); 797 798 //test fail bogus server move 799 try { 800 rsGroupAdmin.moveServersAndTables(Sets.newHashSet(Address.fromString("foo:9999")), 801 Sets.newHashSet(tableName), newGroup.getName()); 802 fail("Bogus servers shouldn't have been successfully moved."); 803 } catch(IOException ex) { 804 String exp = "Source RSGroup for server foo:9999 does not exist."; 805 String msg = "Expected '" + exp + "' in exception message: "; 806 assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp)); 807 } 808 809 //test fail server move 810 try { 811 rsGroupAdmin.moveServersAndTables(Sets.newHashSet(targetServer.getAddress()), 812 Sets.newHashSet(tableName), RSGroupInfo.DEFAULT_GROUP); 813 fail("servers shouldn't have been successfully moved."); 814 } catch(IOException ex) { 815 String exp = "Target RSGroup " + RSGroupInfo.DEFAULT_GROUP + 816 " is same as source " + RSGroupInfo.DEFAULT_GROUP + " RSGroup."; 817 String msg = "Expected '" + exp + "' in exception message: "; 818 assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp)); 819 } 820 821 //verify default group info 822 Assert.assertEquals(oldDefaultGroupServerSize, 823 rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getServers().size()); 824 Assert.assertEquals(oldDefaultGroupTableSize, 825 rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP).getTables().size()); 826 827 //verify new group info 828 Assert.assertEquals(1, 829 rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers().size()); 830 Assert.assertEquals(0, 831 rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables().size()); 832 833 //get all region to move targetServer 834 List<String> regionList = getTableRegionMap().get(tableName); 835 for(String region : regionList) { 836 // Lets move this region to the targetServer 837 TEST_UTIL.getAdmin().move(Bytes.toBytes(RegionInfo.encodeRegionName(Bytes.toBytes(region))), 838 Bytes.toBytes(targetServer.getServerName())); 839 } 840 841 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 842 @Override 843 public boolean evaluate() throws Exception { 844 return getTableRegionMap().get(tableName) != null && 845 getTableRegionMap().get(tableName).size() == 5 && 846 getTableServerRegionMap().get(tableName).size() == 1 && 847 admin.getClusterMetrics(EnumSet.of(Option.REGIONS_IN_TRANSITION)) 848 .getRegionStatesInTransition().size() < 1; 849 } 850 }); 851 852 //verify that all region move to targetServer 853 Assert.assertEquals(5, getTableServerRegionMap().get(tableName).get(targetServer).size()); 854 855 //move targetServer and table to newGroup 856 LOG.info("moving server and table to newGroup"); 857 rsGroupAdmin.moveServersAndTables(Sets.newHashSet(targetServer.getAddress()), 858 Sets.newHashSet(tableName), newGroup.getName()); 859 860 //verify group change 861 Assert.assertEquals(newGroup.getName(), 862 rsGroupAdmin.getRSGroupInfoOfTable(tableName).getName()); 863 864 //verify servers' not exist in old group 865 Set<Address> defaultServers = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP) 866 .getServers(); 867 assertFalse(defaultServers.contains(targetServer.getAddress())); 868 869 //verify servers' exist in new group 870 Set<Address> newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers(); 871 assertTrue(newGroupServers.contains(targetServer.getAddress())); 872 873 //verify tables' not exist in old group 874 Set<TableName> defaultTables = rsGroupAdmin.getRSGroupInfo(RSGroupInfo.DEFAULT_GROUP) 875 .getTables(); 876 assertFalse(defaultTables.contains(tableName)); 877 878 //verify tables' exist in new group 879 Set<TableName> newGroupTables = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getTables(); 880 assertTrue(newGroupTables.contains(tableName)); 881 882 //verify that all region still assgin on targetServer 883 Assert.assertEquals(5, getTableServerRegionMap().get(tableName).get(targetServer).size()); 884 } 885 886 @Test 887 public void testClearDeadServers() throws Exception { 888 LOG.info("testClearDeadServers"); 889 final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 3); 890 NUM_DEAD_SERVERS = cluster.getClusterMetrics().getDeadServerNames().size(); 891 892 ServerName targetServer = ServerName.parseServerName( 893 newGroup.getServers().iterator().next().toString()); 894 AdminProtos.AdminService.BlockingInterface targetRS = 895 ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); 896 try { 897 targetServer = ProtobufUtil.toServerName(targetRS.getServerInfo(null, 898 GetServerInfoRequest.newBuilder().build()).getServerInfo().getServerName()); 899 //stopping may cause an exception 900 //due to the connection loss 901 targetRS.stopServer(null, 902 AdminProtos.StopServerRequest.newBuilder().setReason("Die").build()); 903 NUM_DEAD_SERVERS ++; 904 } catch(Exception e) { 905 } 906 //wait for stopped regionserver to dead server list 907 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 908 @Override 909 public boolean evaluate() throws Exception { 910 return !master.getServerManager().areDeadServersInProgress() 911 && cluster.getClusterMetrics().getDeadServerNames().size() == NUM_DEAD_SERVERS; 912 } 913 }); 914 assertFalse(cluster.getClusterMetrics().getLiveServerMetrics().containsKey(targetServer)); 915 assertTrue(cluster.getClusterMetrics().getDeadServerNames().contains(targetServer)); 916 assertTrue(newGroup.getServers().contains(targetServer.getAddress())); 917 918 //clear dead servers list 919 List<ServerName> notClearedServers = admin.clearDeadServers(Lists.newArrayList(targetServer)); 920 assertEquals(0, notClearedServers.size()); 921 922 Set<Address> newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers(); 923 assertFalse(newGroupServers.contains(targetServer.getAddress())); 924 assertEquals(2, newGroupServers.size()); 925 } 926 927 @Test 928 public void testRemoveServers() throws Exception { 929 LOG.info("testRemoveServers"); 930 final RSGroupInfo newGroup = addGroup(getGroupName(name.getMethodName()), 3); 931 Iterator<Address> iterator = newGroup.getServers().iterator(); 932 ServerName targetServer = ServerName.parseServerName(iterator.next().toString()); 933 934 // remove online servers 935 try { 936 rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress())); 937 fail("Online servers shouldn't have been successfully removed."); 938 } catch(IOException ex) { 939 String exp = "Server " + targetServer.getAddress() 940 + " is an online server, not allowed to remove."; 941 String msg = "Expected '" + exp + "' in exception message: "; 942 assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp)); 943 } 944 assertTrue(newGroup.getServers().contains(targetServer.getAddress())); 945 946 // remove dead servers 947 NUM_DEAD_SERVERS = cluster.getClusterMetrics().getDeadServerNames().size(); 948 AdminProtos.AdminService.BlockingInterface targetRS = 949 ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); 950 try { 951 targetServer = ProtobufUtil.toServerName(targetRS.getServerInfo(null, 952 GetServerInfoRequest.newBuilder().build()).getServerInfo().getServerName()); 953 //stopping may cause an exception 954 //due to the connection loss 955 LOG.info("stopping server " + targetServer.getHostAndPort()); 956 targetRS.stopServer(null, 957 AdminProtos.StopServerRequest.newBuilder().setReason("Die").build()); 958 NUM_DEAD_SERVERS ++; 959 } catch(Exception e) { 960 } 961 962 //wait for stopped regionserver to dead server list 963 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 964 @Override 965 public boolean evaluate() throws Exception { 966 return !master.getServerManager().areDeadServersInProgress() 967 && cluster.getClusterMetrics().getDeadServerNames().size() == NUM_DEAD_SERVERS; 968 } 969 }); 970 971 try { 972 rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress())); 973 fail("Dead servers shouldn't have been successfully removed."); 974 } catch(IOException ex) { 975 String exp = "Server " + targetServer.getAddress() + " is on the dead servers list," 976 + " Maybe it will come back again, not allowed to remove."; 977 String msg = "Expected '" + exp + "' in exception message: "; 978 assertTrue(msg + " " + ex.getMessage(), ex.getMessage().contains(exp)); 979 } 980 assertTrue(newGroup.getServers().contains(targetServer.getAddress())); 981 982 // remove decommissioned servers 983 List<ServerName> serversToDecommission = new ArrayList<>(); 984 targetServer = ServerName.parseServerName(iterator.next().toString()); 985 targetRS = ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); 986 targetServer = ProtobufUtil.toServerName(targetRS.getServerInfo(null, 987 GetServerInfoRequest.newBuilder().build()).getServerInfo().getServerName()); 988 assertTrue(master.getServerManager().getOnlineServers().containsKey(targetServer)); 989 serversToDecommission.add(targetServer); 990 991 admin.decommissionRegionServers(serversToDecommission, true); 992 assertEquals(1, admin.listDecommissionedRegionServers().size()); 993 994 assertTrue(newGroup.getServers().contains(targetServer.getAddress())); 995 rsGroupAdmin.removeServers(Sets.newHashSet(targetServer.getAddress())); 996 Set<Address> newGroupServers = rsGroupAdmin.getRSGroupInfo(newGroup.getName()).getServers(); 997 assertFalse(newGroupServers.contains(targetServer.getAddress())); 998 assertEquals(2, newGroupServers.size()); 999 } 1000 1001 @Test 1002 public void testCreateWhenRsgroupNoOnlineServers() throws Exception { 1003 LOG.info("testCreateWhenRsgroupNoOnlineServers"); 1004 1005 // set rsgroup has no online servers and test create table 1006 final RSGroupInfo appInfo = addGroup("appInfo", 1); 1007 Iterator<Address> iterator = appInfo.getServers().iterator(); 1008 List<ServerName> serversToDecommission = new ArrayList<>(); 1009 ServerName targetServer = ServerName.parseServerName(iterator.next().toString()); 1010 AdminProtos.AdminService.BlockingInterface targetRS = 1011 ((ClusterConnection) admin.getConnection()).getAdmin(targetServer); 1012 targetServer = ProtobufUtil.toServerName( 1013 targetRS.getServerInfo(null, GetServerInfoRequest.newBuilder().build()).getServerInfo() 1014 .getServerName()); 1015 assertTrue(master.getServerManager().getOnlineServers().containsKey(targetServer)); 1016 serversToDecommission.add(targetServer); 1017 admin.decommissionRegionServers(serversToDecommission, true); 1018 assertEquals(1, admin.listDecommissionedRegionServers().size()); 1019 1020 final TableName tableName = TableName.valueOf(tablePrefix + "_ns", name.getMethodName()); 1021 admin.createNamespace(NamespaceDescriptor.create(tableName.getNamespaceAsString()) 1022 .addConfiguration(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP, appInfo.getName()).build()); 1023 final HTableDescriptor desc = new HTableDescriptor(tableName); 1024 desc.addFamily(new HColumnDescriptor("f")); 1025 try { 1026 admin.createTable(desc); 1027 fail("Shouldn't create table successfully!"); 1028 } catch (Exception e) { 1029 LOG.debug("create table error", e); 1030 } 1031 1032 // recommission and test create table 1033 admin.recommissionRegionServer(targetServer, null); 1034 assertEquals(0, admin.listDecommissionedRegionServers().size()); 1035 admin.createTable(desc); 1036 // wait for created table to be assigned 1037 TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() { 1038 @Override public boolean evaluate() throws Exception { 1039 return getTableRegionMap().get(desc.getTableName()) != null; 1040 } 1041 }); 1042 } 1043}