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