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