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