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.master.balancer; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023 024import java.io.FileNotFoundException; 025import java.io.IOException; 026import java.security.SecureRandom; 027import java.util.ArrayList; 028import java.util.Collections; 029import java.util.HashMap; 030import java.util.HashSet; 031import java.util.Iterator; 032import java.util.List; 033import java.util.Map; 034import java.util.Set; 035import java.util.TreeMap; 036import java.util.TreeSet; 037import org.apache.commons.lang3.StringUtils; 038import org.apache.hadoop.conf.Configuration; 039import org.apache.hadoop.hbase.HBaseClassTestRule; 040import org.apache.hadoop.hbase.HBaseConfiguration; 041import org.apache.hadoop.hbase.HTableDescriptor; 042import org.apache.hadoop.hbase.ServerName; 043import org.apache.hadoop.hbase.TableDescriptors; 044import org.apache.hadoop.hbase.TableName; 045import org.apache.hadoop.hbase.client.RegionInfo; 046import org.apache.hadoop.hbase.client.RegionInfoBuilder; 047import org.apache.hadoop.hbase.master.HMaster; 048import org.apache.hadoop.hbase.master.LoadBalancer; 049import org.apache.hadoop.hbase.master.MasterServices; 050import org.apache.hadoop.hbase.master.RegionPlan; 051import org.apache.hadoop.hbase.master.assignment.AssignmentManager; 052import org.apache.hadoop.hbase.master.assignment.RegionStates; 053import org.apache.hadoop.hbase.net.Address; 054import org.apache.hadoop.hbase.rsgroup.RSGroupBasedLoadBalancer; 055import org.apache.hadoop.hbase.rsgroup.RSGroupInfo; 056import org.apache.hadoop.hbase.rsgroup.RSGroupInfoManager; 057import org.apache.hadoop.hbase.testclassification.SmallTests; 058import org.apache.hadoop.hbase.util.Bytes; 059import org.junit.BeforeClass; 060import org.junit.ClassRule; 061import org.junit.Test; 062import org.junit.experimental.categories.Category; 063import org.mockito.Mockito; 064import org.mockito.invocation.InvocationOnMock; 065import org.mockito.stubbing.Answer; 066import org.slf4j.Logger; 067import org.slf4j.LoggerFactory; 068 069import org.apache.hbase.thirdparty.com.google.common.collect.ArrayListMultimap; 070import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 071 072//TODO use stochastic based load balancer instead 073@Category(SmallTests.class) 074public class TestRSGroupBasedLoadBalancer { 075 076 @ClassRule 077 public static final HBaseClassTestRule CLASS_RULE = 078 HBaseClassTestRule.forClass(TestRSGroupBasedLoadBalancer.class); 079 080 private static final Logger LOG = LoggerFactory.getLogger(TestRSGroupBasedLoadBalancer.class); 081 private static RSGroupBasedLoadBalancer loadBalancer; 082 private static SecureRandom rand; 083 084 static String[] groups = new String[] { RSGroupInfo.DEFAULT_GROUP, "dg2", "dg3", "dg4" }; 085 static TableName table0 = TableName.valueOf("dt0"); 086 static TableName[] tables = 087 new TableName[] { TableName.valueOf("dt1"), 088 TableName.valueOf("dt2"), 089 TableName.valueOf("dt3"), 090 TableName.valueOf("dt4")}; 091 static List<ServerName> servers; 092 static Map<String, RSGroupInfo> groupMap; 093 static Map<TableName, String> tableMap; 094 static List<HTableDescriptor> tableDescs; 095 int[] regionAssignment = new int[] { 2, 5, 7, 10, 4, 3, 1 }; 096 static int regionId = 0; 097 098 @BeforeClass 099 public static void beforeAllTests() throws Exception { 100 rand = new SecureRandom(); 101 servers = generateServers(7); 102 groupMap = constructGroupInfo(servers, groups); 103 tableMap = new HashMap<>(); 104 tableDescs = constructTableDesc(); 105 Configuration conf = HBaseConfiguration.create(); 106 conf.set("hbase.regions.slop", "0"); 107 conf.set("hbase.rsgroup.grouploadbalancer.class", SimpleLoadBalancer.class.getCanonicalName()); 108 loadBalancer = new RSGroupBasedLoadBalancer(); 109 loadBalancer.setRsGroupInfoManager(getMockedGroupInfoManager()); 110 loadBalancer.setMasterServices(getMockedMaster()); 111 loadBalancer.setConf(conf); 112 loadBalancer.initialize(); 113 } 114 115 /** 116 * Test the load balancing algorithm. 117 * 118 * Invariant is that all servers of the group should be hosting either floor(average) or 119 * ceiling(average) 120 */ 121 @Test 122 public void testBalanceCluster() throws Exception { 123 Map<ServerName, List<RegionInfo>> servers = mockClusterServers(); 124 ArrayListMultimap<String, ServerAndLoad> list = convertToGroupBasedMap(servers); 125 LOG.info("Mock Cluster : " + printStats(list)); 126 List<RegionPlan> plans = loadBalancer.balanceCluster(servers); 127 ArrayListMultimap<String, ServerAndLoad> balancedCluster = reconcile( 128 list, plans); 129 LOG.info("Mock Balance : " + printStats(balancedCluster)); 130 assertClusterAsBalanced(balancedCluster); 131 } 132 133 /** 134 * Invariant is that all servers of a group have load between floor(avg) and 135 * ceiling(avg) number of regions. 136 */ 137 private void assertClusterAsBalanced( 138 ArrayListMultimap<String, ServerAndLoad> groupLoadMap) { 139 for (String gName : groupLoadMap.keySet()) { 140 List<ServerAndLoad> groupLoad = groupLoadMap.get(gName); 141 int numServers = groupLoad.size(); 142 int numRegions = 0; 143 int maxRegions = 0; 144 int minRegions = Integer.MAX_VALUE; 145 for (ServerAndLoad server : groupLoad) { 146 int nr = server.getLoad(); 147 if (nr > maxRegions) { 148 maxRegions = nr; 149 } 150 if (nr < minRegions) { 151 minRegions = nr; 152 } 153 numRegions += nr; 154 } 155 if (maxRegions - minRegions < 2) { 156 // less than 2 between max and min, can't balance 157 return; 158 } 159 int min = numRegions / numServers; 160 int max = numRegions % numServers == 0 ? min : min + 1; 161 162 for (ServerAndLoad server : groupLoad) { 163 assertTrue(server.getLoad() <= max); 164 assertTrue(server.getLoad() >= min); 165 } 166 } 167 } 168 169 /** 170 * All regions have an assignment. 171 */ 172 private void assertImmediateAssignment(List<RegionInfo> regions, 173 List<ServerName> servers, 174 Map<RegionInfo, ServerName> assignments) 175 throws IOException { 176 for (RegionInfo region : regions) { 177 assertTrue(assignments.containsKey(region)); 178 ServerName server = assignments.get(region); 179 TableName tableName = region.getTable(); 180 181 String groupName = getMockedGroupInfoManager().getRSGroupOfTable(tableName); 182 assertTrue(StringUtils.isNotEmpty(groupName)); 183 RSGroupInfo gInfo = getMockedGroupInfoManager().getRSGroup(groupName); 184 assertTrue("Region is not correctly assigned to group servers.", 185 gInfo.containsServer(server.getAddress())); 186 } 187 } 188 189 /** 190 * Tests the bulk assignment used during cluster startup. 191 * 192 * Round-robin. Should yield a balanced cluster so same invariant as the 193 * load balancer holds, all servers holding either floor(avg) or 194 * ceiling(avg). 195 */ 196 @Test 197 public void testBulkAssignment() throws Exception { 198 List<RegionInfo> regions = randomRegions(25); 199 Map<ServerName, List<RegionInfo>> assignments = loadBalancer 200 .roundRobinAssignment(regions, servers); 201 //test empty region/servers scenario 202 //this should not throw an NPE 203 loadBalancer.roundRobinAssignment(regions, 204 Collections.EMPTY_LIST); 205 //test regular scenario 206 assertTrue(assignments.keySet().size() == servers.size()); 207 for (ServerName sn : assignments.keySet()) { 208 List<RegionInfo> regionAssigned = assignments.get(sn); 209 for (RegionInfo region : regionAssigned) { 210 TableName tableName = region.getTable(); 211 String groupName = 212 getMockedGroupInfoManager().getRSGroupOfTable(tableName); 213 assertTrue(StringUtils.isNotEmpty(groupName)); 214 RSGroupInfo gInfo = getMockedGroupInfoManager().getRSGroup( 215 groupName); 216 assertTrue( 217 "Region is not correctly assigned to group servers.", 218 gInfo.containsServer(sn.getAddress())); 219 } 220 } 221 ArrayListMultimap<String, ServerAndLoad> loadMap = convertToGroupBasedMap(assignments); 222 assertClusterAsBalanced(loadMap); 223 } 224 225 /** 226 * Test the cluster startup bulk assignment which attempts to retain assignment info. 227 */ 228 @Test 229 public void testRetainAssignment() throws Exception { 230 // Test simple case where all same servers are there 231 Map<ServerName, List<RegionInfo>> currentAssignments = mockClusterServers(); 232 Map<RegionInfo, ServerName> inputForTest = new HashMap<>(); 233 for (ServerName sn : currentAssignments.keySet()) { 234 for (RegionInfo region : currentAssignments.get(sn)) { 235 inputForTest.put(region, sn); 236 } 237 } 238 //verify region->null server assignment is handled 239 inputForTest.put(randomRegions(1).get(0), null); 240 Map<ServerName, List<RegionInfo>> newAssignment = loadBalancer 241 .retainAssignment(inputForTest, servers); 242 assertRetainedAssignment(inputForTest, servers, newAssignment); 243 } 244 245 /** 246 * Test BOGUS_SERVER_NAME among groups do not overwrite each other. 247 */ 248 @Test 249 public void testRoundRobinAssignment() throws Exception { 250 List<ServerName> onlineServers = new ArrayList<ServerName>(servers.size()); 251 onlineServers.addAll(servers); 252 List<RegionInfo> regions = randomRegions(25); 253 int bogusRegion = 0; 254 for(RegionInfo region : regions){ 255 String group = tableMap.get(region.getTable()); 256 if("dg3".equals(group) || "dg4".equals(group)){ 257 bogusRegion++; 258 } 259 } 260 Set<Address> offlineServers = new HashSet<Address>(); 261 offlineServers.addAll(groupMap.get("dg3").getServers()); 262 offlineServers.addAll(groupMap.get("dg4").getServers()); 263 for(Iterator<ServerName> it = onlineServers.iterator(); it.hasNext();){ 264 ServerName server = it.next(); 265 Address address = server.getAddress(); 266 if(offlineServers.contains(address)){ 267 it.remove(); 268 } 269 } 270 Map<ServerName, List<RegionInfo>> assignments = loadBalancer 271 .roundRobinAssignment(regions, onlineServers); 272 assertEquals(bogusRegion, assignments.get(LoadBalancer.BOGUS_SERVER_NAME).size()); 273 } 274 275 /** 276 * Asserts a valid retained assignment plan. 277 * <p> 278 * Must meet the following conditions: 279 * <ul> 280 * <li>Every input region has an assignment, and to an online server 281 * <li>If a region had an existing assignment to a server with the same 282 * address a a currently online server, it will be assigned to it 283 * </ul> 284 */ 285 private void assertRetainedAssignment( 286 Map<RegionInfo, ServerName> existing, List<ServerName> servers, 287 Map<ServerName, List<RegionInfo>> assignment) 288 throws FileNotFoundException, IOException { 289 // Verify condition 1, every region assigned, and to online server 290 Set<ServerName> onlineServerSet = new TreeSet<>(servers); 291 Set<RegionInfo> assignedRegions = new TreeSet<>(RegionInfo.COMPARATOR); 292 for (Map.Entry<ServerName, List<RegionInfo>> a : assignment.entrySet()) { 293 assertTrue( 294 "Region assigned to server that was not listed as online", 295 onlineServerSet.contains(a.getKey())); 296 for (RegionInfo r : a.getValue()) { 297 assignedRegions.add(r); 298 } 299 } 300 assertEquals(existing.size(), assignedRegions.size()); 301 302 // Verify condition 2, every region must be assigned to correct server. 303 Set<String> onlineHostNames = new TreeSet<>(); 304 for (ServerName s : servers) { 305 onlineHostNames.add(s.getHostname()); 306 } 307 308 for (Map.Entry<ServerName, List<RegionInfo>> a : assignment.entrySet()) { 309 ServerName currentServer = a.getKey(); 310 for (RegionInfo r : a.getValue()) { 311 ServerName oldAssignedServer = existing.get(r); 312 TableName tableName = r.getTable(); 313 String groupName = 314 getMockedGroupInfoManager().getRSGroupOfTable(tableName); 315 assertTrue(StringUtils.isNotEmpty(groupName)); 316 RSGroupInfo gInfo = getMockedGroupInfoManager().getRSGroup( 317 groupName); 318 assertTrue( 319 "Region is not correctly assigned to group servers.", 320 gInfo.containsServer(currentServer.getAddress())); 321 if (oldAssignedServer != null 322 && onlineHostNames.contains(oldAssignedServer 323 .getHostname())) { 324 // this region was previously assigned somewhere, and that 325 // host is still around, then the host must have been is a 326 // different group. 327 if (!oldAssignedServer.getAddress().equals(currentServer.getAddress())) { 328 assertFalse(gInfo.containsServer(oldAssignedServer.getAddress())); 329 } 330 } 331 } 332 } 333 } 334 335 private String printStats( 336 ArrayListMultimap<String, ServerAndLoad> groupBasedLoad) { 337 StringBuffer sb = new StringBuffer(); 338 sb.append("\n"); 339 for (String groupName : groupBasedLoad.keySet()) { 340 sb.append("Stats for group: " + groupName); 341 sb.append("\n"); 342 sb.append(groupMap.get(groupName).getServers()); 343 sb.append("\n"); 344 List<ServerAndLoad> groupLoad = groupBasedLoad.get(groupName); 345 int numServers = groupLoad.size(); 346 int totalRegions = 0; 347 sb.append("Per Server Load: \n"); 348 for (ServerAndLoad sLoad : groupLoad) { 349 sb.append("Server :" + sLoad.getServerName() + " Load : " 350 + sLoad.getLoad() + "\n"); 351 totalRegions += sLoad.getLoad(); 352 } 353 sb.append(" Group Statistics : \n"); 354 float average = (float) totalRegions / numServers; 355 int max = (int) Math.ceil(average); 356 int min = (int) Math.floor(average); 357 sb.append("[srvr=" + numServers + " rgns=" + totalRegions + " avg=" 358 + average + " max=" + max + " min=" + min + "]"); 359 sb.append("\n"); 360 sb.append("==============================="); 361 sb.append("\n"); 362 } 363 return sb.toString(); 364 } 365 366 private ArrayListMultimap<String, ServerAndLoad> convertToGroupBasedMap( 367 final Map<ServerName, List<RegionInfo>> serversMap) throws IOException { 368 ArrayListMultimap<String, ServerAndLoad> loadMap = ArrayListMultimap 369 .create(); 370 for (RSGroupInfo gInfo : getMockedGroupInfoManager().listRSGroups()) { 371 Set<Address> groupServers = gInfo.getServers(); 372 for (Address hostPort : groupServers) { 373 ServerName actual = null; 374 for(ServerName entry: servers) { 375 if(entry.getAddress().equals(hostPort)) { 376 actual = entry; 377 break; 378 } 379 } 380 List<RegionInfo> regions = serversMap.get(actual); 381 assertTrue("No load for " + actual, regions != null); 382 loadMap.put(gInfo.getName(), 383 new ServerAndLoad(actual, regions.size())); 384 } 385 } 386 return loadMap; 387 } 388 389 private ArrayListMultimap<String, ServerAndLoad> reconcile( 390 ArrayListMultimap<String, ServerAndLoad> previousLoad, 391 List<RegionPlan> plans) { 392 ArrayListMultimap<String, ServerAndLoad> result = ArrayListMultimap 393 .create(); 394 result.putAll(previousLoad); 395 if (plans != null) { 396 for (RegionPlan plan : plans) { 397 ServerName source = plan.getSource(); 398 updateLoad(result, source, -1); 399 ServerName destination = plan.getDestination(); 400 updateLoad(result, destination, +1); 401 } 402 } 403 return result; 404 } 405 406 private void updateLoad( 407 ArrayListMultimap<String, ServerAndLoad> previousLoad, 408 final ServerName sn, final int diff) { 409 for (String groupName : previousLoad.keySet()) { 410 ServerAndLoad newSAL = null; 411 ServerAndLoad oldSAL = null; 412 for (ServerAndLoad sal : previousLoad.get(groupName)) { 413 if (ServerName.isSameAddress(sn, sal.getServerName())) { 414 oldSAL = sal; 415 newSAL = new ServerAndLoad(sn, sal.getLoad() + diff); 416 break; 417 } 418 } 419 if (newSAL != null) { 420 previousLoad.remove(groupName, oldSAL); 421 previousLoad.put(groupName, newSAL); 422 break; 423 } 424 } 425 } 426 427 private Map<ServerName, List<RegionInfo>> mockClusterServers() throws IOException { 428 assertTrue(servers.size() == regionAssignment.length); 429 Map<ServerName, List<RegionInfo>> assignment = new TreeMap<>(); 430 for (int i = 0; i < servers.size(); i++) { 431 int numRegions = regionAssignment[i]; 432 List<RegionInfo> regions = assignedRegions(numRegions, servers.get(i)); 433 assignment.put(servers.get(i), regions); 434 } 435 return assignment; 436 } 437 438 /** 439 * Generate a list of regions evenly distributed between the tables. 440 * 441 * @param numRegions The number of regions to be generated. 442 * @return List of RegionInfo. 443 */ 444 private List<RegionInfo> randomRegions(int numRegions) { 445 List<RegionInfo> regions = new ArrayList<>(numRegions); 446 byte[] start = new byte[16]; 447 byte[] end = new byte[16]; 448 rand.nextBytes(start); 449 rand.nextBytes(end); 450 int regionIdx = rand.nextInt(tables.length); 451 for (int i = 0; i < numRegions; i++) { 452 Bytes.putInt(start, 0, numRegions << 1); 453 Bytes.putInt(end, 0, (numRegions << 1) + 1); 454 int tableIndex = (i + regionIdx) % tables.length; 455 regions.add(RegionInfoBuilder.newBuilder(tables[tableIndex]) 456 .setStartKey(start) 457 .setEndKey(end) 458 .setSplit(false) 459 .setRegionId(regionId++) 460 .build()); 461 } 462 return regions; 463 } 464 465 /** 466 * Generate assigned regions to a given server using group information. 467 * 468 * @param numRegions the num regions to generate 469 * @param sn the servername 470 * @return the list of regions 471 * @throws java.io.IOException Signals that an I/O exception has occurred. 472 */ 473 private List<RegionInfo> assignedRegions(int numRegions, ServerName sn) throws IOException { 474 List<RegionInfo> regions = new ArrayList<>(numRegions); 475 byte[] start = new byte[16]; 476 byte[] end = new byte[16]; 477 Bytes.putInt(start, 0, numRegions << 1); 478 Bytes.putInt(end, 0, (numRegions << 1) + 1); 479 for (int i = 0; i < numRegions; i++) { 480 TableName tableName = getTableName(sn); 481 regions.add(RegionInfoBuilder.newBuilder(tableName) 482 .setStartKey(start) 483 .setEndKey(end) 484 .setSplit(false) 485 .setRegionId(regionId++) 486 .build()); 487 } 488 return regions; 489 } 490 491 private static List<ServerName> generateServers(int numServers) { 492 List<ServerName> servers = new ArrayList<>(numServers); 493 for (int i = 0; i < numServers; i++) { 494 String host = "server" + rand.nextInt(100000); 495 int port = rand.nextInt(60000); 496 servers.add(ServerName.valueOf(host, port, -1)); 497 } 498 return servers; 499 } 500 501 /** 502 * Construct group info, with each group having at least one server. 503 * 504 * @param servers the servers 505 * @param groups the groups 506 * @return the map 507 */ 508 private static Map<String, RSGroupInfo> constructGroupInfo( 509 List<ServerName> servers, String[] groups) { 510 assertTrue(servers != null); 511 assertTrue(servers.size() >= groups.length); 512 int index = 0; 513 Map<String, RSGroupInfo> groupMap = new HashMap<>(); 514 for (String grpName : groups) { 515 RSGroupInfo RSGroupInfo = new RSGroupInfo(grpName); 516 RSGroupInfo.addServer(servers.get(index).getAddress()); 517 groupMap.put(grpName, RSGroupInfo); 518 index++; 519 } 520 while (index < servers.size()) { 521 int grpIndex = rand.nextInt(groups.length); 522 groupMap.get(groups[grpIndex]).addServer( 523 servers.get(index).getAddress()); 524 index++; 525 } 526 return groupMap; 527 } 528 529 /** 530 * Construct table descriptors evenly distributed between the groups. 531 * 532 * @return the list 533 */ 534 private static List<HTableDescriptor> constructTableDesc() { 535 List<HTableDescriptor> tds = Lists.newArrayList(); 536 int index = rand.nextInt(groups.length); 537 for (int i = 0; i < tables.length; i++) { 538 HTableDescriptor htd = new HTableDescriptor(tables[i]); 539 int grpIndex = (i + index) % groups.length ; 540 String groupName = groups[grpIndex]; 541 tableMap.put(tables[i], groupName); 542 tds.add(htd); 543 } 544 tableMap.put(table0, ""); 545 tds.add(new HTableDescriptor(table0)); 546 return tds; 547 } 548 549 private static MasterServices getMockedMaster() throws IOException { 550 TableDescriptors tds = Mockito.mock(TableDescriptors.class); 551 Mockito.when(tds.get(tables[0])).thenReturn(tableDescs.get(0)); 552 Mockito.when(tds.get(tables[1])).thenReturn(tableDescs.get(1)); 553 Mockito.when(tds.get(tables[2])).thenReturn(tableDescs.get(2)); 554 Mockito.when(tds.get(tables[3])).thenReturn(tableDescs.get(3)); 555 MasterServices services = Mockito.mock(HMaster.class); 556 Mockito.when(services.getTableDescriptors()).thenReturn(tds); 557 AssignmentManager am = Mockito.mock(AssignmentManager.class); 558 Mockito.when(services.getAssignmentManager()).thenReturn(am); 559 RegionStates rss = Mockito.mock(RegionStates.class); 560 Mockito.when(am.getRegionStates()).thenReturn(rss); 561 return services; 562 } 563 564 private static RSGroupInfoManager getMockedGroupInfoManager() throws IOException { 565 RSGroupInfoManager gm = Mockito.mock(RSGroupInfoManager.class); 566 Mockito.when(gm.getRSGroup(groups[0])).thenReturn( 567 groupMap.get(groups[0])); 568 Mockito.when(gm.getRSGroup(groups[1])).thenReturn( 569 groupMap.get(groups[1])); 570 Mockito.when(gm.getRSGroup(groups[2])).thenReturn( 571 groupMap.get(groups[2])); 572 Mockito.when(gm.getRSGroup(groups[3])).thenReturn( 573 groupMap.get(groups[3])); 574 Mockito.when(gm.listRSGroups()).thenReturn( 575 Lists.newLinkedList(groupMap.values())); 576 Mockito.when(gm.isOnline()).thenReturn(true); 577 Mockito.when(gm.getRSGroupOfTable(Mockito.any())) 578 .thenAnswer(new Answer<String>() { 579 @Override 580 public String answer(InvocationOnMock invocation) throws Throwable { 581 return tableMap.get(invocation.getArgument(0)); 582 } 583 }); 584 return gm; 585 } 586 587 private TableName getTableName(ServerName sn) throws IOException { 588 TableName tableName = null; 589 RSGroupInfoManager gm = getMockedGroupInfoManager(); 590 RSGroupInfo groupOfServer = null; 591 for(RSGroupInfo gInfo : gm.listRSGroups()){ 592 if(gInfo.containsServer(sn.getAddress())){ 593 groupOfServer = gInfo; 594 break; 595 } 596 } 597 598 for(HTableDescriptor desc : tableDescs){ 599 if(gm.getRSGroupOfTable(desc.getTableName()).endsWith(groupOfServer.getName())){ 600 tableName = desc.getTableName(); 601 } 602 } 603 return tableName; 604 } 605}