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.assignment; 019 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.Collections; 023import java.util.Comparator; 024import java.util.HashMap; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028import java.util.SortedSet; 029import java.util.TreeSet; 030import java.util.concurrent.ConcurrentHashMap; 031import java.util.concurrent.ConcurrentSkipListMap; 032import java.util.concurrent.atomic.AtomicInteger; 033import java.util.function.Predicate; 034import java.util.stream.Collectors; 035import org.apache.hadoop.hbase.HRegionLocation; 036import org.apache.hadoop.hbase.ServerName; 037import org.apache.hadoop.hbase.TableName; 038import org.apache.hadoop.hbase.client.RegionInfo; 039import org.apache.hadoop.hbase.client.TableState; 040import org.apache.hadoop.hbase.master.RegionState; 041import org.apache.hadoop.hbase.master.RegionState.State; 042import org.apache.hadoop.hbase.master.TableStateManager; 043import org.apache.hadoop.hbase.util.Bytes; 044import org.apache.yetus.audience.InterfaceAudience; 045import org.slf4j.Logger; 046import org.slf4j.LoggerFactory; 047 048import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; 049 050/** 051 * RegionStates contains a set of Maps that describes the in-memory state of the AM, with 052 * the regions available in the system, the region in transition, the offline regions and 053 * the servers holding regions. 054 */ 055@InterfaceAudience.Private 056public class RegionStates { 057 private static final Logger LOG = LoggerFactory.getLogger(RegionStates.class); 058 059 // This comparator sorts the RegionStates by time stamp then Region name. 060 // Comparing by timestamp alone can lead us to discard different RegionStates that happen 061 // to share a timestamp. 062 private static class RegionStateStampComparator implements Comparator<RegionState> { 063 @Override 064 public int compare(final RegionState l, final RegionState r) { 065 int stampCmp = Long.compare(l.getStamp(), r.getStamp()); 066 return stampCmp != 0 ? stampCmp : RegionInfo.COMPARATOR.compare(l.getRegion(), r.getRegion()); 067 } 068 } 069 070 public final static RegionStateStampComparator REGION_STATE_STAMP_COMPARATOR = 071 new RegionStateStampComparator(); 072 073 // TODO: Replace the ConcurrentSkipListMaps 074 /** 075 * RegionName -- i.e. RegionInfo.getRegionName() -- as bytes to {@link RegionStateNode} 076 */ 077 private final ConcurrentSkipListMap<byte[], RegionStateNode> regionsMap = 078 new ConcurrentSkipListMap<byte[], RegionStateNode>(Bytes.BYTES_COMPARATOR); 079 080 private final ConcurrentSkipListMap<RegionInfo, RegionStateNode> regionInTransition = 081 new ConcurrentSkipListMap<RegionInfo, RegionStateNode>(RegionInfo.COMPARATOR); 082 083 /** 084 * Regions marked as offline on a read of hbase:meta. Unused or at least, once 085 * offlined, regions have no means of coming on line again. TODO. 086 */ 087 private final ConcurrentSkipListMap<RegionInfo, RegionStateNode> regionOffline = 088 new ConcurrentSkipListMap<RegionInfo, RegionStateNode>(); 089 090 private final ConcurrentSkipListMap<byte[], RegionFailedOpen> regionFailedOpen = 091 new ConcurrentSkipListMap<byte[], RegionFailedOpen>(Bytes.BYTES_COMPARATOR); 092 093 private final ConcurrentHashMap<ServerName, ServerStateNode> serverMap = 094 new ConcurrentHashMap<ServerName, ServerStateNode>(); 095 096 public RegionStates() { } 097 098 /** 099 * Called on stop of AssignmentManager. 100 */ 101 public void clear() { 102 regionsMap.clear(); 103 regionInTransition.clear(); 104 regionOffline.clear(); 105 serverMap.clear(); 106 } 107 108 @VisibleForTesting 109 public boolean isRegionInRegionStates(final RegionInfo hri) { 110 return (regionsMap.containsKey(hri.getRegionName()) || regionInTransition.containsKey(hri) 111 || regionOffline.containsKey(hri)); 112 } 113 114 // ========================================================================== 115 // RegionStateNode helpers 116 // ========================================================================== 117 @VisibleForTesting 118 RegionStateNode createRegionStateNode(RegionInfo regionInfo) { 119 RegionStateNode newNode = new RegionStateNode(regionInfo, regionInTransition); 120 RegionStateNode oldNode = regionsMap.putIfAbsent(regionInfo.getRegionName(), newNode); 121 return oldNode != null ? oldNode : newNode; 122 } 123 124 public RegionStateNode getOrCreateRegionStateNode(RegionInfo regionInfo) { 125 RegionStateNode node = getRegionStateNodeFromName(regionInfo.getRegionName()); 126 return node != null ? node : createRegionStateNode(regionInfo); 127 } 128 129 RegionStateNode getRegionStateNodeFromName(byte[] regionName) { 130 return regionsMap.get(regionName); 131 } 132 133 public RegionStateNode getRegionStateNode(RegionInfo regionInfo) { 134 return getRegionStateNodeFromName(regionInfo.getRegionName()); 135 } 136 137 public void deleteRegion(final RegionInfo regionInfo) { 138 regionsMap.remove(regionInfo.getRegionName()); 139 // See HBASE-20860 140 // After master restarts, merged regions' RIT state may not be cleaned, 141 // making sure they are cleaned here 142 if (regionInTransition.containsKey(regionInfo)) { 143 regionInTransition.remove(regionInfo); 144 } 145 // Remove from the offline regions map too if there. 146 if (this.regionOffline.containsKey(regionInfo)) { 147 if (LOG.isTraceEnabled()) LOG.trace("Removing from regionOffline Map: " + regionInfo); 148 this.regionOffline.remove(regionInfo); 149 } 150 } 151 152 public void deleteRegions(final List<RegionInfo> regionInfos) { 153 regionInfos.forEach(this::deleteRegion); 154 } 155 156 ArrayList<RegionStateNode> getTableRegionStateNodes(final TableName tableName) { 157 final ArrayList<RegionStateNode> regions = new ArrayList<RegionStateNode>(); 158 for (RegionStateNode node: regionsMap.tailMap(tableName.getName()).values()) { 159 if (!node.getTable().equals(tableName)) break; 160 regions.add(node); 161 } 162 return regions; 163 } 164 165 ArrayList<RegionState> getTableRegionStates(final TableName tableName) { 166 final ArrayList<RegionState> regions = new ArrayList<RegionState>(); 167 for (RegionStateNode node: regionsMap.tailMap(tableName.getName()).values()) { 168 if (!node.getTable().equals(tableName)) break; 169 regions.add(node.toRegionState()); 170 } 171 return regions; 172 } 173 174 ArrayList<RegionInfo> getTableRegionsInfo(final TableName tableName) { 175 final ArrayList<RegionInfo> regions = new ArrayList<RegionInfo>(); 176 for (RegionStateNode node: regionsMap.tailMap(tableName.getName()).values()) { 177 if (!node.getTable().equals(tableName)) break; 178 regions.add(node.getRegionInfo()); 179 } 180 return regions; 181 } 182 183 /** @return A view of region state nodes for all the regions. */ 184 public Collection<RegionStateNode> getRegionStateNodes() { 185 return Collections.unmodifiableCollection(regionsMap.values()); 186 } 187 188 /** @return A snapshot of region state nodes for all the regions. */ 189 public ArrayList<RegionState> getRegionStates() { 190 final ArrayList<RegionState> regions = new ArrayList<>(regionsMap.size()); 191 for (RegionStateNode node: regionsMap.values()) { 192 regions.add(node.toRegionState()); 193 } 194 return regions; 195 } 196 197 // ========================================================================== 198 // RegionState helpers 199 // ========================================================================== 200 public RegionState getRegionState(final RegionInfo regionInfo) { 201 RegionStateNode regionStateNode = getRegionStateNode(regionInfo); 202 return regionStateNode == null ? null : regionStateNode.toRegionState(); 203 } 204 205 public RegionState getRegionState(final String encodedRegionName) { 206 // TODO: Need a map <encodedName, ...> but it is just dispatch merge... 207 for (RegionStateNode node: regionsMap.values()) { 208 if (node.getRegionInfo().getEncodedName().equals(encodedRegionName)) { 209 return node.toRegionState(); 210 } 211 } 212 return null; 213 } 214 215 // ============================================================================================ 216 // TODO: helpers 217 // ============================================================================================ 218 public boolean hasTableRegionStates(final TableName tableName) { 219 // TODO 220 return !getTableRegionStates(tableName).isEmpty(); 221 } 222 223 /** 224 * @return Return online regions of table; does not include OFFLINE or SPLITTING regions. 225 */ 226 public List<RegionInfo> getRegionsOfTable(final TableName table) { 227 return getRegionsOfTable(table, false); 228 } 229 230 private HRegionLocation createRegionForReopen(RegionStateNode node) { 231 node.lock(); 232 try { 233 if (!include(node, false)) { 234 return null; 235 } 236 if (node.isInState(State.OPEN)) { 237 return new HRegionLocation(node.getRegionInfo(), node.getRegionLocation(), 238 node.getOpenSeqNum()); 239 } else if (node.isInState(State.OPENING)) { 240 return new HRegionLocation(node.getRegionInfo(), node.getRegionLocation(), -1); 241 } else { 242 return null; 243 } 244 } finally { 245 node.unlock(); 246 } 247 } 248 249 /** 250 * Get the regions to be reopened when modifying a table. 251 * <p/> 252 * Notice that the {@code openSeqNum} in the returned HRegionLocation is also used to indicate the 253 * state of this region, positive means the region is in {@link State#OPEN}, -1 means 254 * {@link State#OPENING}. And for regions in other states we do not need reopen them. 255 */ 256 public List<HRegionLocation> getRegionsOfTableForReopen(TableName tableName) { 257 return getTableRegionStateNodes(tableName).stream().map(this::createRegionForReopen) 258 .filter(r -> r != null).collect(Collectors.toList()); 259 } 260 261 /** 262 * Check whether the region has been reopened. The meaning of the {@link HRegionLocation} is the 263 * same with {@link #getRegionsOfTableForReopen(TableName)}. 264 * <p/> 265 * For a region which is in {@link State#OPEN} before, if the region state is changed or the open 266 * seq num is changed, we can confirm that it has been reopened. 267 * <p/> 268 * For a region which is in {@link State#OPENING} before, usually it will be in {@link State#OPEN} 269 * now and we will schedule a MRP to reopen it. But there are several exceptions: 270 * <ul> 271 * <li>The region is in state other than {@link State#OPEN} or {@link State#OPENING}.</li> 272 * <li>The location of the region has been changed</li> 273 * </ul> 274 * Of course the region could still be in {@link State#OPENING} state and still on the same 275 * server, then here we will still return a {@link HRegionLocation} for it, just like 276 * {@link #getRegionsOfTableForReopen(TableName)}. 277 * @param oldLoc the previous state/location of this region 278 * @return null if the region has been reopened, otherwise a new {@link HRegionLocation} which 279 * means we still need to reopen the region. 280 * @see #getRegionsOfTableForReopen(TableName) 281 */ 282 public HRegionLocation checkReopened(HRegionLocation oldLoc) { 283 RegionStateNode node = getRegionStateNode(oldLoc.getRegion()); 284 // HBASE-20921 285 // if the oldLoc's state node does not exist, that means the region is 286 // merged or split, no need to check it 287 if (node == null) { 288 return null; 289 } 290 node.lock(); 291 try { 292 if (oldLoc.getSeqNum() >= 0) { 293 // in OPEN state before 294 if (node.isInState(State.OPEN)) { 295 if (node.getOpenSeqNum() > oldLoc.getSeqNum()) { 296 // normal case, the region has been reopened 297 return null; 298 } else { 299 // the open seq num does not change, need to reopen again 300 return new HRegionLocation(node.getRegionInfo(), node.getRegionLocation(), 301 node.getOpenSeqNum()); 302 } 303 } else { 304 // the state has been changed so we can make sure that the region has been reopened(not 305 // finished maybe, but not a problem). 306 return null; 307 } 308 } else { 309 // in OPENING state before 310 if (!node.isInState(State.OPEN, State.OPENING)) { 311 // not in OPEN or OPENING state, then we can make sure that the region has been 312 // reopened(not finished maybe, but not a problem) 313 return null; 314 } else { 315 if (!node.getRegionLocation().equals(oldLoc.getServerName())) { 316 // the region has been moved, so we can make sure that the region has been reopened. 317 return null; 318 } 319 // normal case, we are still in OPENING state, or the reopen has been opened and the state 320 // is changed to OPEN. 321 long openSeqNum = node.isInState(State.OPEN) ? node.getOpenSeqNum() : -1; 322 return new HRegionLocation(node.getRegionInfo(), node.getRegionLocation(), openSeqNum); 323 } 324 } 325 } finally { 326 node.unlock(); 327 } 328 } 329 330 /** 331 * @return Return online regions of table; does not include OFFLINE or SPLITTING regions. 332 */ 333 public List<RegionInfo> getRegionsOfTable(TableName table, boolean offline) { 334 return getRegionsOfTable(table, state -> include(state, offline)); 335 } 336 337 /** 338 * @return Return the regions of the table; does not include OFFLINE unless you set 339 * <code>offline</code> to true. Does not include regions that are in the 340 * {@link State#SPLIT} state. 341 */ 342 private List<RegionInfo> getRegionsOfTable(TableName table, Predicate<RegionStateNode> filter) { 343 return getTableRegionStateNodes(table).stream().filter(filter).map(n -> n.getRegionInfo()) 344 .collect(Collectors.toList()); 345 } 346 347 /** 348 * Utility. Whether to include region in list of regions. Default is to 349 * weed out split and offline regions. 350 * @return True if we should include the <code>node</code> (do not include 351 * if split or offline unless <code>offline</code> is set to true. 352 */ 353 boolean include(final RegionStateNode node, final boolean offline) { 354 if (LOG.isTraceEnabled()) { 355 LOG.trace("WORKING ON " + node + " " + node.getRegionInfo()); 356 } 357 final RegionInfo hri = node.getRegionInfo(); 358 if (node.isInState(State.SPLIT) || hri.isSplit()) { 359 return false; 360 } 361 if ((node.isInState(State.OFFLINE) || hri.isOffline()) && !offline) { 362 return false; 363 } 364 return (!hri.isOffline() && !hri.isSplit()) || 365 ((hri.isOffline() || hri.isSplit()) && offline); 366 } 367 368 // ============================================================================================ 369 // Split helpers 370 // These methods will only be called in ServerCrashProcedure, and at the end of SCP we will remove 371 // the ServerStateNode by calling removeServer. 372 // ============================================================================================ 373 374 private void setServerState(ServerName serverName, ServerState state) { 375 ServerStateNode serverNode = getOrCreateServer(serverName); 376 synchronized (serverNode) { 377 serverNode.setState(state); 378 } 379 } 380 381 /** 382 * Call this when we start meta log splitting a crashed Server. 383 * @see #metaLogSplit(ServerName) 384 */ 385 public void metaLogSplitting(ServerName serverName) { 386 setServerState(serverName, ServerState.SPLITTING_META); 387 } 388 389 /** 390 * Called after we've split the meta logs on a crashed Server. 391 * @see #metaLogSplitting(ServerName) 392 */ 393 public void metaLogSplit(ServerName serverName) { 394 setServerState(serverName, ServerState.SPLITTING_META_DONE); 395 } 396 397 /** 398 * Call this when we start log splitting for a crashed Server. 399 * @see #logSplit(ServerName) 400 */ 401 public void logSplitting(final ServerName serverName) { 402 setServerState(serverName, ServerState.SPLITTING); 403 } 404 405 /** 406 * Called after we've split all logs on a crashed Server. 407 * @see #logSplitting(ServerName) 408 */ 409 public void logSplit(final ServerName serverName) { 410 setServerState(serverName, ServerState.OFFLINE); 411 } 412 413 public void updateRegionState(RegionInfo regionInfo, State state) { 414 RegionStateNode regionNode = getOrCreateRegionStateNode(regionInfo); 415 regionNode.lock(); 416 try { 417 regionNode.setState(state); 418 } finally { 419 regionNode.unlock(); 420 } 421 } 422 423 // ============================================================================================ 424 // TODO: 425 // ============================================================================================ 426 public List<RegionInfo> getAssignedRegions() { 427 final List<RegionInfo> result = new ArrayList<RegionInfo>(); 428 for (RegionStateNode node: regionsMap.values()) { 429 if (!node.isInTransition()) { 430 result.add(node.getRegionInfo()); 431 } 432 } 433 return result; 434 } 435 436 public boolean isRegionInState(RegionInfo regionInfo, State... state) { 437 RegionStateNode regionNode = getRegionStateNode(regionInfo); 438 if (regionNode != null) { 439 regionNode.lock(); 440 try { 441 return regionNode.isInState(state); 442 } finally { 443 regionNode.unlock(); 444 } 445 } 446 return false; 447 } 448 449 public boolean isRegionOnline(final RegionInfo regionInfo) { 450 return isRegionInState(regionInfo, State.OPEN); 451 } 452 453 /** 454 * @return True if region is offline (In OFFLINE or CLOSED state). 455 */ 456 public boolean isRegionOffline(final RegionInfo regionInfo) { 457 return isRegionInState(regionInfo, State.OFFLINE, State.CLOSED); 458 } 459 460 public Map<ServerName, List<RegionInfo>> getSnapShotOfAssignment( 461 final Collection<RegionInfo> regions) { 462 final Map<ServerName, List<RegionInfo>> result = new HashMap<ServerName, List<RegionInfo>>(); 463 if (regions != null) { 464 for (RegionInfo hri : regions) { 465 final RegionStateNode node = getRegionStateNode(hri); 466 if (node == null) { 467 continue; 468 } 469 createSnapshot(node, result); 470 } 471 } else { 472 for (RegionStateNode node : regionsMap.values()) { 473 if (node == null) { 474 continue; 475 } 476 createSnapshot(node, result); 477 } 478 } 479 return result; 480 } 481 482 private void createSnapshot(RegionStateNode node, Map<ServerName, List<RegionInfo>> result) { 483 final ServerName serverName = node.getRegionLocation(); 484 if (serverName == null) { 485 return; 486 } 487 488 List<RegionInfo> serverRegions = result.get(serverName); 489 if (serverRegions == null) { 490 serverRegions = new ArrayList<RegionInfo>(); 491 result.put(serverName, serverRegions); 492 } 493 serverRegions.add(node.getRegionInfo()); 494 } 495 496 public Map<RegionInfo, ServerName> getRegionAssignments() { 497 final HashMap<RegionInfo, ServerName> assignments = new HashMap<RegionInfo, ServerName>(); 498 for (RegionStateNode node: regionsMap.values()) { 499 assignments.put(node.getRegionInfo(), node.getRegionLocation()); 500 } 501 return assignments; 502 } 503 504 public Map<RegionState.State, List<RegionInfo>> getRegionByStateOfTable(TableName tableName) { 505 final State[] states = State.values(); 506 final Map<RegionState.State, List<RegionInfo>> tableRegions = 507 new HashMap<State, List<RegionInfo>>(states.length); 508 for (int i = 0; i < states.length; ++i) { 509 tableRegions.put(states[i], new ArrayList<RegionInfo>()); 510 } 511 512 for (RegionStateNode node: regionsMap.values()) { 513 if (node.getTable().equals(tableName)) { 514 tableRegions.get(node.getState()).add(node.getRegionInfo()); 515 } 516 } 517 return tableRegions; 518 } 519 520 public ServerName getRegionServerOfRegion(RegionInfo regionInfo) { 521 RegionStateNode regionNode = getRegionStateNode(regionInfo); 522 if (regionNode != null) { 523 regionNode.lock(); 524 try { 525 ServerName server = regionNode.getRegionLocation(); 526 return server != null ? server : regionNode.getLastHost(); 527 } finally { 528 regionNode.unlock(); 529 } 530 } 531 return null; 532 } 533 534 /** 535 * This is an EXPENSIVE clone. Cloning though is the safest thing to do. 536 * Can't let out original since it can change and at least the load balancer 537 * wants to iterate this exported list. We need to synchronize on regions 538 * since all access to this.servers is under a lock on this.regions. 539 * 540 * @return A clone of current assignments. 541 */ 542 public Map<TableName, Map<ServerName, List<RegionInfo>>> getAssignmentsForBalancer( 543 TableStateManager tableStateManager, List<ServerName> onlineServers) { 544 final Map<TableName, Map<ServerName, List<RegionInfo>>> result = new HashMap<>(); 545 for (RegionStateNode node : regionsMap.values()) { 546 if (isTableDisabled(tableStateManager, node.getTable())) { 547 continue; 548 } 549 if (node.getRegionInfo().isSplitParent()) { 550 continue; 551 } 552 Map<ServerName, List<RegionInfo>> tableResult = 553 result.computeIfAbsent(node.getTable(), t -> new HashMap<>()); 554 final ServerName serverName = node.getRegionLocation(); 555 if (serverName == null) { 556 LOG.info("Skipping, no server for " + node); 557 continue; 558 } 559 List<RegionInfo> serverResult = 560 tableResult.computeIfAbsent(serverName, s -> new ArrayList<>()); 561 serverResult.add(node.getRegionInfo()); 562 } 563 // Add online servers with no assignment for the table. 564 for (Map<ServerName, List<RegionInfo>> table : result.values()) { 565 for (ServerName serverName : onlineServers) { 566 table.computeIfAbsent(serverName, key -> new ArrayList<>()); 567 } 568 } 569 return result; 570 } 571 572 private boolean isTableDisabled(final TableStateManager tableStateManager, 573 final TableName tableName) { 574 return tableStateManager.isTableState(tableName, TableState.State.DISABLED, 575 TableState.State.DISABLING); 576 } 577 578 // ========================================================================== 579 // Region in transition helpers 580 // ========================================================================== 581 public boolean hasRegionsInTransition() { 582 return !regionInTransition.isEmpty(); 583 } 584 585 public boolean isRegionInTransition(final RegionInfo regionInfo) { 586 final RegionStateNode node = regionInTransition.get(regionInfo); 587 return node != null ? node.isInTransition() : false; 588 } 589 590 public RegionState getRegionTransitionState(RegionInfo hri) { 591 RegionStateNode node = regionInTransition.get(hri); 592 if (node == null) { 593 return null; 594 } 595 596 node.lock(); 597 try { 598 return node.isInTransition() ? node.toRegionState() : null; 599 } finally { 600 node.unlock(); 601 } 602 } 603 604 public List<RegionStateNode> getRegionsInTransition() { 605 return new ArrayList<RegionStateNode>(regionInTransition.values()); 606 } 607 608 /** 609 * Get the number of regions in transition. 610 */ 611 public int getRegionsInTransitionCount() { 612 return regionInTransition.size(); 613 } 614 615 public List<RegionState> getRegionsStateInTransition() { 616 final List<RegionState> rit = new ArrayList<RegionState>(regionInTransition.size()); 617 for (RegionStateNode node: regionInTransition.values()) { 618 rit.add(node.toRegionState()); 619 } 620 return rit; 621 } 622 623 public SortedSet<RegionState> getRegionsInTransitionOrderedByTimestamp() { 624 final SortedSet<RegionState> rit = new TreeSet<RegionState>(REGION_STATE_STAMP_COMPARATOR); 625 for (RegionStateNode node: regionInTransition.values()) { 626 rit.add(node.toRegionState()); 627 } 628 return rit; 629 } 630 631 // ========================================================================== 632 // Region offline helpers 633 // ========================================================================== 634 // TODO: Populated when we read meta but regions never make it out of here. 635 public void addToOfflineRegions(final RegionStateNode regionNode) { 636 LOG.info("Added to offline, CURRENTLY NEVER CLEARED!!! " + regionNode); 637 regionOffline.put(regionNode.getRegionInfo(), regionNode); 638 } 639 640 // TODO: Unused. 641 public void removeFromOfflineRegions(final RegionInfo regionInfo) { 642 regionOffline.remove(regionInfo); 643 } 644 645 // ========================================================================== 646 // Region FAIL_OPEN helpers 647 // ========================================================================== 648 public static final class RegionFailedOpen { 649 private final RegionStateNode regionNode; 650 651 private volatile Exception exception = null; 652 private AtomicInteger retries = new AtomicInteger(); 653 654 public RegionFailedOpen(final RegionStateNode regionNode) { 655 this.regionNode = regionNode; 656 } 657 658 public RegionStateNode getRegionStateNode() { 659 return regionNode; 660 } 661 662 public RegionInfo getRegionInfo() { 663 return regionNode.getRegionInfo(); 664 } 665 666 public int incrementAndGetRetries() { 667 return this.retries.incrementAndGet(); 668 } 669 670 public int getRetries() { 671 return retries.get(); 672 } 673 674 public void setException(final Exception exception) { 675 this.exception = exception; 676 } 677 678 public Exception getException() { 679 return this.exception; 680 } 681 } 682 683 public RegionFailedOpen addToFailedOpen(final RegionStateNode regionNode) { 684 final byte[] key = regionNode.getRegionInfo().getRegionName(); 685 RegionFailedOpen node = regionFailedOpen.get(key); 686 if (node == null) { 687 RegionFailedOpen newNode = new RegionFailedOpen(regionNode); 688 RegionFailedOpen oldNode = regionFailedOpen.putIfAbsent(key, newNode); 689 node = oldNode != null ? oldNode : newNode; 690 } 691 return node; 692 } 693 694 public RegionFailedOpen getFailedOpen(final RegionInfo regionInfo) { 695 return regionFailedOpen.get(regionInfo.getRegionName()); 696 } 697 698 public void removeFromFailedOpen(final RegionInfo regionInfo) { 699 regionFailedOpen.remove(regionInfo.getRegionName()); 700 } 701 702 public List<RegionState> getRegionFailedOpen() { 703 if (regionFailedOpen.isEmpty()) return Collections.emptyList(); 704 705 ArrayList<RegionState> regions = new ArrayList<RegionState>(regionFailedOpen.size()); 706 for (RegionFailedOpen r: regionFailedOpen.values()) { 707 regions.add(r.getRegionStateNode().toRegionState()); 708 } 709 return regions; 710 } 711 712 // ========================================================================== 713 // Servers 714 // ========================================================================== 715 716 /** 717 * Be judicious calling this method. Do it on server register ONLY otherwise 718 * you could mess up online server accounting. TOOD: Review usage and convert 719 * to {@link #getServerNode(ServerName)} where we can. 720 */ 721 public ServerStateNode getOrCreateServer(final ServerName serverName) { 722 ServerStateNode node = serverMap.get(serverName); 723 if (node == null) { 724 node = new ServerStateNode(serverName); 725 ServerStateNode oldNode = serverMap.putIfAbsent(serverName, node); 726 node = oldNode != null ? oldNode : node; 727 } 728 return node; 729 } 730 731 /** 732 * Called by SCP at end of successful processing. 733 */ 734 public void removeServer(final ServerName serverName) { 735 serverMap.remove(serverName); 736 } 737 738 /** 739 * @return Pertinent ServerStateNode or NULL if none found (Do not make modifications). 740 */ 741 @VisibleForTesting 742 public ServerStateNode getServerNode(final ServerName serverName) { 743 return serverMap.get(serverName); 744 } 745 746 public double getAverageLoad() { 747 int numServers = 0; 748 int totalLoad = 0; 749 for (ServerStateNode node: serverMap.values()) { 750 totalLoad += node.getRegionCount(); 751 numServers++; 752 } 753 return numServers == 0 ? 0.0: (double)totalLoad / (double)numServers; 754 } 755 756 public ServerStateNode addRegionToServer(final RegionStateNode regionNode) { 757 ServerStateNode serverNode = getOrCreateServer(regionNode.getRegionLocation()); 758 serverNode.addRegion(regionNode); 759 return serverNode; 760 } 761 762 public ServerStateNode removeRegionFromServer(final ServerName serverName, 763 final RegionStateNode regionNode) { 764 ServerStateNode serverNode = getOrCreateServer(serverName); 765 serverNode.removeRegion(regionNode); 766 return serverNode; 767 } 768 769 // ========================================================================== 770 // ToString helpers 771 // ========================================================================== 772 public static String regionNamesToString(final Collection<byte[]> regions) { 773 final StringBuilder sb = new StringBuilder(); 774 final Iterator<byte[]> it = regions.iterator(); 775 sb.append("["); 776 if (it.hasNext()) { 777 sb.append(Bytes.toStringBinary(it.next())); 778 while (it.hasNext()) { 779 sb.append(", "); 780 sb.append(Bytes.toStringBinary(it.next())); 781 } 782 } 783 sb.append("]"); 784 return sb.toString(); 785 } 786}