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