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