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 java.io.IOException; 021import java.util.ArrayList; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.HashSet; 025import java.util.Iterator; 026import java.util.LinkedList; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030 031import org.apache.commons.lang3.StringUtils; 032import org.apache.hadoop.hbase.NamespaceDescriptor; 033import org.apache.hadoop.hbase.ServerName; 034import org.apache.hadoop.hbase.TableName; 035import org.apache.hadoop.hbase.client.RegionInfo; 036import org.apache.hadoop.hbase.constraint.ConstraintException; 037import org.apache.hadoop.hbase.master.HMaster; 038import org.apache.hadoop.hbase.master.LoadBalancer; 039import org.apache.hadoop.hbase.master.MasterServices; 040import org.apache.hadoop.hbase.master.RegionPlan; 041import org.apache.hadoop.hbase.master.RegionState; 042import org.apache.hadoop.hbase.master.ServerManager; 043import org.apache.hadoop.hbase.master.assignment.AssignmentManager; 044import org.apache.hadoop.hbase.master.assignment.RegionStates.RegionStateNode; 045import org.apache.hadoop.hbase.net.Address; 046import org.apache.yetus.audience.InterfaceAudience; 047import org.slf4j.Logger; 048import org.slf4j.LoggerFactory; 049 050import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 051import org.apache.hbase.thirdparty.com.google.common.collect.Maps; 052 053/** 054 * Service to support Region Server Grouping (HBase-6721). 055 */ 056@InterfaceAudience.Private 057public class RSGroupAdminServer implements RSGroupAdmin { 058 private static final Logger LOG = LoggerFactory.getLogger(RSGroupAdminServer.class); 059 public static final String KEEP_ONE_SERVER_IN_DEFAULT_ERROR_MESSAGE = "should keep at least " + 060 "one server in 'default' RSGroup."; 061 062 private MasterServices master; 063 private final RSGroupInfoManager rsGroupInfoManager; 064 065 public RSGroupAdminServer(MasterServices master, RSGroupInfoManager rsGroupInfoManager) 066 throws IOException { 067 this.master = master; 068 this.rsGroupInfoManager = rsGroupInfoManager; 069 } 070 071 @Override 072 public RSGroupInfo getRSGroupInfo(String groupName) throws IOException { 073 return rsGroupInfoManager.getRSGroup(groupName); 074 } 075 076 @Override 077 public RSGroupInfo getRSGroupInfoOfTable(TableName tableName) throws IOException { 078 // We are reading across two Maps in the below with out synchronizing across 079 // them; should be safe most of the time. 080 String groupName = rsGroupInfoManager.getRSGroupOfTable(tableName); 081 return groupName == null? null: rsGroupInfoManager.getRSGroup(groupName); 082 } 083 084 private void checkOnlineServersOnly(Set<Address> servers) throws ConstraintException { 085 // This uglyness is because we only have Address, not ServerName. 086 // Online servers are keyed by ServerName. 087 Set<Address> onlineServers = new HashSet<>(); 088 for(ServerName server: master.getServerManager().getOnlineServers().keySet()) { 089 onlineServers.add(server.getAddress()); 090 } 091 for (Address address: servers) { 092 if (!onlineServers.contains(address)) { 093 throw new ConstraintException( 094 "Server " + address + " is not an online server in 'default' RSGroup."); 095 } 096 } 097 } 098 099 /** 100 * Check passed name. Fail if nulls or if corresponding RSGroupInfo not found. 101 * @return The RSGroupInfo named <code>name</code> 102 */ 103 private RSGroupInfo getAndCheckRSGroupInfo(String name) throws IOException { 104 if (StringUtils.isEmpty(name)) { 105 throw new ConstraintException("RSGroup cannot be null."); 106 } 107 RSGroupInfo rsGroupInfo = getRSGroupInfo(name); 108 if (rsGroupInfo == null) { 109 throw new ConstraintException("RSGroup does not exist: " + name); 110 } 111 return rsGroupInfo; 112 } 113 114 /** 115 * @return List of Regions associated with this <code>server</code>. 116 */ 117 private List<RegionInfo> getRegions(final Address server) { 118 LinkedList<RegionInfo> regions = new LinkedList<>(); 119 for (Map.Entry<RegionInfo, ServerName> el : 120 master.getAssignmentManager().getRegionStates().getRegionAssignments().entrySet()) { 121 if (el.getValue() == null) { 122 continue; 123 } 124 125 if (el.getValue().getAddress().equals(server)) { 126 addRegion(regions, el.getKey()); 127 } 128 } 129 for (RegionStateNode state : master.getAssignmentManager().getRegionsInTransition()) { 130 if (state.getRegionLocation().getAddress().equals(server)) { 131 addRegion(regions, state.getRegionInfo()); 132 } 133 } 134 return regions; 135 } 136 137 private void addRegion(final LinkedList<RegionInfo> regions, RegionInfo hri) { 138 // If meta, move it last otherwise other unassigns fail because meta is not 139 // online for them to update state in. This is dodgy. Needs to be made more 140 // robust. See TODO below. 141 if (hri.isMetaRegion()) { 142 regions.addLast(hri); 143 } else { 144 regions.addFirst(hri); 145 } 146 } 147 148 /** 149 * Check servers and tables. 150 * 151 * @param servers servers to move 152 * @param tables tables to move 153 * @param targetGroupName target group name 154 * @throws IOException if nulls or if servers and tables not belong to the same group 155 */ 156 private void checkServersAndTables(Set<Address> servers, Set<TableName> tables, 157 String targetGroupName) throws IOException { 158 // Presume first server's source group. Later ensure all servers are from this group. 159 Address firstServer = servers.iterator().next(); 160 RSGroupInfo tmpSrcGrp = rsGroupInfoManager.getRSGroupOfServer(firstServer); 161 if (tmpSrcGrp == null) { 162 // Be careful. This exception message is tested for in TestRSGroupsBase... 163 throw new ConstraintException("Source RSGroup for server " + firstServer 164 + " does not exist."); 165 } 166 RSGroupInfo srcGrp = new RSGroupInfo(tmpSrcGrp); 167 if (srcGrp.getName().equals(targetGroupName)) { 168 throw new ConstraintException("Target RSGroup " + targetGroupName + 169 " is same as source " + srcGrp.getName() + " RSGroup."); 170 } 171 // Only move online servers 172 checkOnlineServersOnly(servers); 173 174 // Ensure all servers are of same rsgroup. 175 for (Address server: servers) { 176 String tmpGroup = rsGroupInfoManager.getRSGroupOfServer(server).getName(); 177 if (!tmpGroup.equals(srcGrp.getName())) { 178 throw new ConstraintException("Move server request should only come from one source " + 179 "RSGroup. Expecting only " + srcGrp.getName() + " but contains " + tmpGroup); 180 } 181 } 182 183 // Ensure all tables and servers are of same rsgroup. 184 for (TableName table : tables) { 185 String tmpGroup = rsGroupInfoManager.getRSGroupOfTable(table); 186 if (!tmpGroup.equals(srcGrp.getName())) { 187 throw new ConstraintException("Move table request should only come from one source " + 188 "RSGroup. Expecting only " + srcGrp.getName() + " but contains " + tmpGroup); 189 } 190 } 191 192 if (srcGrp.getServers().size() <= servers.size() && srcGrp.getTables().size() > tables.size()) { 193 throw new ConstraintException("Cannot leave a RSGroup " + srcGrp.getName() + 194 " that contains tables without servers to host them."); 195 } 196 } 197 198 /** 199 * Moves every region from servers which are currently located on these servers, 200 * but should not be located there. 201 * @param servers the servers that will move to new group 202 * @param tables these tables will be kept on the servers, others will be moved 203 * @param targetGroupName the target group name 204 * @throws IOException if moving the server and tables fail 205 */ 206 private void moveRegionsFromServers(Set<Address> servers, Set<TableName> tables, 207 String targetGroupName) throws IOException { 208 boolean foundRegionsToMove; 209 RSGroupInfo targetGrp = getRSGroupInfo(targetGroupName); 210 Set<Address> allSevers = new HashSet<>(servers); 211 do { 212 foundRegionsToMove = false; 213 for (Iterator<Address> iter = allSevers.iterator(); iter.hasNext();) { 214 Address rs = iter.next(); 215 // Get regions that are associated with this server and filter regions by tables. 216 List<RegionInfo> regions = new ArrayList<>(); 217 for (RegionInfo region : getRegions(rs)) { 218 if (!tables.contains(region.getTable())) { 219 regions.add(region); 220 } 221 } 222 223 LOG.info("Moving " + regions.size() + " region(s) from " + rs + 224 " for server move to " + targetGroupName); 225 if (!regions.isEmpty()) { 226 for (RegionInfo region: regions) { 227 // Regions might get assigned from tables of target group so we need to filter 228 if (!targetGrp.containsTable(region.getTable())) { 229 this.master.getAssignmentManager().move(region); 230 if (master.getAssignmentManager().getRegionStates(). 231 getRegionState(region).isFailedOpen()) { 232 continue; 233 } 234 foundRegionsToMove = true; 235 } 236 } 237 } 238 if (!foundRegionsToMove) { 239 iter.remove(); 240 } 241 } 242 try { 243 rsGroupInfoManager.wait(1000); 244 } catch (InterruptedException e) { 245 LOG.warn("Sleep interrupted", e); 246 Thread.currentThread().interrupt(); 247 } 248 } while (foundRegionsToMove); 249 } 250 251 /** 252 * Moves every region of tables which should be kept on the servers, 253 * but currently they are located on other servers. 254 * @param servers the regions of these servers will be kept on the servers, others will be moved 255 * @param tables the tables that will move to new group 256 * @param targetGroupName the target group name 257 * @throws IOException if moving the region fails 258 */ 259 private void moveRegionsToServers(Set<Address> servers, Set<TableName> tables, 260 String targetGroupName) throws IOException { 261 for (TableName table: tables) { 262 LOG.info("Moving region(s) from " + table + " for table move to " + targetGroupName); 263 for (RegionInfo region : master.getAssignmentManager().getRegionStates() 264 .getRegionsOfTable(table)) { 265 ServerName sn = master.getAssignmentManager().getRegionStates() 266 .getRegionServerOfRegion(region); 267 if (!servers.contains(sn.getAddress())) { 268 master.getAssignmentManager().move(region); 269 } 270 } 271 } 272 } 273 274 @edu.umd.cs.findbugs.annotations.SuppressWarnings( 275 value="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE", 276 justification="Ignoring complaint because don't know what it is complaining about") 277 @Override 278 public void moveServers(Set<Address> servers, String targetGroupName) throws IOException { 279 if (servers == null) { 280 throw new ConstraintException("The list of servers to move cannot be null."); 281 } 282 if (servers.isEmpty()) { 283 // For some reason this difference between null servers and isEmpty is important distinction. 284 // TODO. Why? Stuff breaks if I equate them. 285 return; 286 } 287 RSGroupInfo targetGrp = getAndCheckRSGroupInfo(targetGroupName); 288 289 // Hold a lock on the manager instance while moving servers to prevent 290 // another writer changing our state while we are working. 291 synchronized (rsGroupInfoManager) { 292 // Presume first server's source group. Later ensure all servers are from this group. 293 Address firstServer = servers.iterator().next(); 294 RSGroupInfo srcGrp = rsGroupInfoManager.getRSGroupOfServer(firstServer); 295 if (srcGrp == null) { 296 // Be careful. This exception message is tested for in TestRSGroupsBase... 297 throw new ConstraintException("Source RSGroup for server " + firstServer 298 + " does not exist."); 299 } 300 if (srcGrp.getName().equals(targetGroupName)) { 301 throw new ConstraintException("Target RSGroup " + targetGroupName + 302 " is same as source " + srcGrp + " RSGroup."); 303 } 304 // Only move online servers (when moving from 'default') or servers from other 305 // groups. This prevents bogus servers from entering groups 306 if (RSGroupInfo.DEFAULT_GROUP.equals(srcGrp.getName())) { 307 if (srcGrp.getServers().size() <= servers.size()) { 308 throw new ConstraintException(KEEP_ONE_SERVER_IN_DEFAULT_ERROR_MESSAGE); 309 } 310 checkOnlineServersOnly(servers); 311 } 312 // Ensure all servers are of same rsgroup. 313 for (Address server: servers) { 314 String tmpGroup = rsGroupInfoManager.getRSGroupOfServer(server).getName(); 315 if (!tmpGroup.equals(srcGrp.getName())) { 316 throw new ConstraintException("Move server request should only come from one source " + 317 "RSGroup. Expecting only " + srcGrp.getName() + " but contains " + tmpGroup); 318 } 319 } 320 if (srcGrp.getServers().size() <= servers.size() && srcGrp.getTables().size() > 0) { 321 throw new ConstraintException("Cannot leave a RSGroup " + srcGrp.getName() + 322 " that contains tables without servers to host them."); 323 } 324 325 // MovedServers may be < passed in 'servers'. 326 Set<Address> movedServers = rsGroupInfoManager.moveServers(servers, srcGrp.getName(), 327 targetGroupName); 328 List<Address> editableMovedServers = Lists.newArrayList(movedServers); 329 boolean foundRegionsToMove; 330 do { 331 foundRegionsToMove = false; 332 for (Iterator<Address> iter = editableMovedServers.iterator(); iter.hasNext();) { 333 Address rs = iter.next(); 334 // Get regions that are associated with this server. 335 List<RegionInfo> regions = getRegions(rs); 336 337 LOG.info("Moving " + regions.size() + " region(s) from " + rs + 338 " for server move to " + targetGroupName); 339 340 for (RegionInfo region: regions) { 341 // Regions might get assigned from tables of target group so we need to filter 342 if (targetGrp.containsTable(region.getTable())) { 343 continue; 344 } 345 LOG.info("Moving region " + region.getShortNameToLog()); 346 this.master.getAssignmentManager().move(region); 347 if (master.getAssignmentManager().getRegionStates(). 348 getRegionState(region).isFailedOpen()) { 349 // If region is in FAILED_OPEN state, it won't recover, not without 350 // operator intervention... in hbase-2.0.0 at least. Continue rather 351 // than mark region as 'foundRegionsToMove'. 352 continue; 353 } 354 foundRegionsToMove = true; 355 } 356 if (!foundRegionsToMove) { 357 iter.remove(); 358 } 359 } 360 try { 361 rsGroupInfoManager.wait(1000); 362 } catch (InterruptedException e) { 363 LOG.warn("Sleep interrupted", e); 364 Thread.currentThread().interrupt(); 365 } 366 } while (foundRegionsToMove); 367 368 LOG.info("Move server done: " + srcGrp.getName() + "=>" + targetGroupName); 369 } 370 } 371 372 @Override 373 public void moveTables(Set<TableName> tables, String targetGroup) throws IOException { 374 if (tables == null) { 375 throw new ConstraintException("The list of servers cannot be null."); 376 } 377 if (tables.size() < 1) { 378 LOG.debug("moveTables() passed an empty set. Ignoring."); 379 return; 380 } 381 382 // Hold a lock on the manager instance while moving servers to prevent 383 // another writer changing our state while we are working. 384 synchronized (rsGroupInfoManager) { 385 if(targetGroup != null) { 386 RSGroupInfo destGroup = rsGroupInfoManager.getRSGroup(targetGroup); 387 if(destGroup == null) { 388 throw new ConstraintException("Target " + targetGroup + " RSGroup does not exist."); 389 } 390 if(destGroup.getServers().size() < 1) { 391 throw new ConstraintException("Target RSGroup must have at least one server."); 392 } 393 } 394 395 for (TableName table : tables) { 396 String srcGroup = rsGroupInfoManager.getRSGroupOfTable(table); 397 if(srcGroup != null && srcGroup.equals(targetGroup)) { 398 throw new ConstraintException( 399 "Source RSGroup " + srcGroup + " is same as target " + targetGroup + 400 " RSGroup for table " + table); 401 } 402 LOG.info("Moving table " + table.getNameAsString() + " to RSGroup " + targetGroup); 403 } 404 rsGroupInfoManager.moveTables(tables, targetGroup); 405 406 // targetGroup is null when a table is being deleted. In this case no further 407 // action is required. 408 if (targetGroup != null) { 409 for (TableName table: tables) { 410 if (master.getAssignmentManager().isTableDisabled(table)) { 411 LOG.debug("Skipping move regions because the table" + table + " is disabled."); 412 continue; 413 } 414 for (RegionInfo region : 415 master.getAssignmentManager().getRegionStates().getRegionsOfTable(table)) { 416 LOG.info("Moving region " + region.getShortNameToLog() + 417 " to RSGroup " + targetGroup); 418 master.getAssignmentManager().move(region); 419 } 420 } 421 } 422 } 423 } 424 425 @Override 426 public void addRSGroup(String name) throws IOException { 427 rsGroupInfoManager.addRSGroup(new RSGroupInfo(name)); 428 } 429 430 @Override 431 public void removeRSGroup(String name) throws IOException { 432 // Hold a lock on the manager instance while moving servers to prevent 433 // another writer changing our state while we are working. 434 synchronized (rsGroupInfoManager) { 435 RSGroupInfo rsGroupInfo = rsGroupInfoManager.getRSGroup(name); 436 if (rsGroupInfo == null) { 437 throw new ConstraintException("RSGroup " + name + " does not exist"); 438 } 439 int tableCount = rsGroupInfo.getTables().size(); 440 if (tableCount > 0) { 441 throw new ConstraintException("RSGroup " + name + " has " + tableCount + 442 " tables; you must remove these tables from the rsgroup before " + 443 "the rsgroup can be removed."); 444 } 445 int serverCount = rsGroupInfo.getServers().size(); 446 if (serverCount > 0) { 447 throw new ConstraintException("RSGroup " + name + " has " + serverCount + 448 " servers; you must remove these servers from the RSGroup before" + 449 "the RSGroup can be removed."); 450 } 451 for (NamespaceDescriptor ns: master.getClusterSchema().getNamespaces()) { 452 String nsGroup = ns.getConfigurationValue(rsGroupInfo.NAMESPACE_DESC_PROP_GROUP); 453 if (nsGroup != null && nsGroup.equals(name)) { 454 throw new ConstraintException("RSGroup " + name + " is referenced by namespace: " + 455 ns.getName()); 456 } 457 } 458 rsGroupInfoManager.removeRSGroup(name); 459 } 460 } 461 462 @Override 463 public boolean balanceRSGroup(String groupName) throws IOException { 464 ServerManager serverManager = master.getServerManager(); 465 AssignmentManager assignmentManager = master.getAssignmentManager(); 466 LoadBalancer balancer = master.getLoadBalancer(); 467 468 synchronized (balancer) { 469 // If balance not true, don't run balancer. 470 if (!((HMaster) master).isBalancerOn()) { 471 return false; 472 } 473 474 if (getRSGroupInfo(groupName) == null) { 475 throw new ConstraintException("RSGroup does not exist: "+groupName); 476 } 477 // Only allow one balance run at at time. 478 Map<String, RegionState> groupRIT = rsGroupGetRegionsInTransition(groupName); 479 if (groupRIT.size() > 0) { 480 LOG.debug("Not running balancer because " + groupRIT.size() + " region(s) in transition: " + 481 StringUtils.abbreviate( 482 master.getAssignmentManager().getRegionStates().getRegionsInTransition().toString(), 483 256)); 484 return false; 485 } 486 if (serverManager.areDeadServersInProgress()) { 487 LOG.debug("Not running balancer because processing dead regionserver(s): " + 488 serverManager.getDeadServers()); 489 return false; 490 } 491 492 //We balance per group instead of per table 493 List<RegionPlan> plans = new ArrayList<>(); 494 for(Map.Entry<TableName, Map<ServerName, List<RegionInfo>>> tableMap: 495 getRSGroupAssignmentsByTable(groupName).entrySet()) { 496 LOG.info("Creating partial plan for table " + tableMap.getKey() + ": " 497 + tableMap.getValue()); 498 List<RegionPlan> partialPlans = balancer.balanceCluster(tableMap.getValue()); 499 LOG.info("Partial plan for table " + tableMap.getKey() + ": " + partialPlans); 500 if (partialPlans != null) { 501 plans.addAll(partialPlans); 502 } 503 } 504 long startTime = System.currentTimeMillis(); 505 boolean balancerRan = !plans.isEmpty(); 506 if (balancerRan) { 507 LOG.info("RSGroup balance " + groupName + " starting with plan count: " + plans.size()); 508 for (RegionPlan plan: plans) { 509 LOG.info("balance " + plan); 510 assignmentManager.moveAsync(plan); 511 } 512 LOG.info("RSGroup balance " + groupName + " completed after " + 513 (System.currentTimeMillis()-startTime) + " seconds"); 514 } 515 return balancerRan; 516 } 517 } 518 519 @Override 520 public List<RSGroupInfo> listRSGroups() throws IOException { 521 return rsGroupInfoManager.listRSGroups(); 522 } 523 524 @Override 525 public RSGroupInfo getRSGroupOfServer(Address hostPort) throws IOException { 526 return rsGroupInfoManager.getRSGroupOfServer(hostPort); 527 } 528 529 @Override 530 public void moveServersAndTables(Set<Address> servers, Set<TableName> tables, String targetGroup) 531 throws IOException { 532 if (servers == null || servers.isEmpty()) { 533 throw new ConstraintException("The list of servers to move cannot be null or empty."); 534 } 535 if (tables == null || tables.isEmpty()) { 536 throw new ConstraintException("The list of tables to move cannot be null or empty."); 537 } 538 539 //check target group 540 getAndCheckRSGroupInfo(targetGroup); 541 542 // Hold a lock on the manager instance while moving servers and tables to prevent 543 // another writer changing our state while we are working. 544 synchronized (rsGroupInfoManager) { 545 //check servers and tables status 546 checkServersAndTables(servers, tables, targetGroup); 547 548 //Move servers and tables to a new group. 549 String srcGroup = getRSGroupOfServer(servers.iterator().next()).getName(); 550 rsGroupInfoManager.moveServersAndTables(servers, tables, srcGroup, targetGroup); 551 552 //move regions which should not belong to these tables 553 moveRegionsFromServers(servers, tables, targetGroup); 554 //move regions which should belong to these servers 555 moveRegionsToServers(servers, tables, targetGroup); 556 } 557 LOG.info("Move servers and tables done. Severs :" 558 + servers + " , Tables : " + tables + " => " + targetGroup); 559 } 560 561 @Override 562 public void removeServers(Set<Address> servers) throws IOException { 563 { 564 if (servers == null || servers.isEmpty()) { 565 throw new ConstraintException("The set of servers to remove cannot be null or empty."); 566 } 567 // Hold a lock on the manager instance while moving servers to prevent 568 // another writer changing our state while we are working. 569 synchronized (rsGroupInfoManager) { 570 //check the set of servers 571 checkForDeadOrOnlineServers(servers); 572 rsGroupInfoManager.removeServers(servers); 573 LOG.info("Remove decommissioned servers " + servers + " from rsgroup done."); 574 } 575 } 576 } 577 578 private Map<String, RegionState> rsGroupGetRegionsInTransition(String groupName) 579 throws IOException { 580 Map<String, RegionState> rit = Maps.newTreeMap(); 581 AssignmentManager am = master.getAssignmentManager(); 582 for(TableName tableName : getRSGroupInfo(groupName).getTables()) { 583 for(RegionInfo regionInfo: am.getRegionStates().getRegionsOfTable(tableName)) { 584 RegionState state = am.getRegionStates().getRegionTransitionState(regionInfo); 585 if(state != null) { 586 rit.put(regionInfo.getEncodedName(), state); 587 } 588 } 589 } 590 return rit; 591 } 592 593 private Map<TableName, Map<ServerName, List<RegionInfo>>> 594 getRSGroupAssignmentsByTable(String groupName) throws IOException { 595 Map<TableName, Map<ServerName, List<RegionInfo>>> result = Maps.newHashMap(); 596 RSGroupInfo rsGroupInfo = getRSGroupInfo(groupName); 597 Map<TableName, Map<ServerName, List<RegionInfo>>> assignments = Maps.newHashMap(); 598 for(Map.Entry<RegionInfo, ServerName> entry: 599 master.getAssignmentManager().getRegionStates().getRegionAssignments().entrySet()) { 600 TableName currTable = entry.getKey().getTable(); 601 ServerName currServer = entry.getValue(); 602 RegionInfo currRegion = entry.getKey(); 603 if (rsGroupInfo.getTables().contains(currTable)) { 604 assignments.putIfAbsent(currTable, new HashMap<>()); 605 assignments.get(currTable).putIfAbsent(currServer, new ArrayList<>()); 606 assignments.get(currTable).get(currServer).add(currRegion); 607 } 608 } 609 610 Map<ServerName, List<RegionInfo>> serverMap = Maps.newHashMap(); 611 for(ServerName serverName: master.getServerManager().getOnlineServers().keySet()) { 612 if(rsGroupInfo.getServers().contains(serverName.getAddress())) { 613 serverMap.put(serverName, Collections.emptyList()); 614 } 615 } 616 617 // add all tables that are members of the group 618 for(TableName tableName : rsGroupInfo.getTables()) { 619 if(assignments.containsKey(tableName)) { 620 result.put(tableName, new HashMap<>()); 621 result.get(tableName).putAll(serverMap); 622 result.get(tableName).putAll(assignments.get(tableName)); 623 LOG.debug("Adding assignments for " + tableName + ": " + assignments.get(tableName)); 624 } 625 } 626 627 return result; 628 } 629 630 /** 631 * Check if the set of servers are belong to dead servers list or online servers list. 632 * @param servers servers to remove 633 */ 634 private void checkForDeadOrOnlineServers(Set<Address> servers) throws ConstraintException { 635 // This uglyness is because we only have Address, not ServerName. 636 Set<Address> onlineServers = new HashSet<>(); 637 List<ServerName> drainingServers = master.getServerManager().getDrainingServersList(); 638 for (ServerName server : master.getServerManager().getOnlineServers().keySet()) { 639 // Only online but not decommissioned servers are really online 640 if (!drainingServers.contains(server)) { 641 onlineServers.add(server.getAddress()); 642 } 643 } 644 645 Set<Address> deadServers = new HashSet<>(); 646 for(ServerName server: master.getServerManager().getDeadServers().copyServerNames()) { 647 deadServers.add(server.getAddress()); 648 } 649 650 for (Address address: servers) { 651 if (onlineServers.contains(address)) { 652 throw new ConstraintException( 653 "Server " + address + " is an online server, not allowed to remove."); 654 } 655 if (deadServers.contains(address)) { 656 throw new ConstraintException( 657 "Server " + address + " is on the dead servers list," 658 + " Maybe it will come back again, not allowed to remove."); 659 } 660 } 661 } 662}