/** * Get the HRegionInfo from cache, if not there, from the hbase:meta table. Be careful. Does RPC. * Do not hold a lock or synchronize when you call this method. * * @param regionName * @return HRegionInfo for the region */ @SuppressWarnings("deprecation") protected HRegionInfo getRegionInfo(final byte[] regionName) { String encodedName = HRegionInfo.encodeRegionName(regionName); RegionState regionState = getRegionState(encodedName); if (regionState != null) { return regionState.getRegion(); } try { Pair<HRegionInfo, ServerName> p = MetaTableAccessor.getRegion(server.getConnection(), regionName); HRegionInfo hri = p == null ? null : p.getFirst(); if (hri != null) { createRegionState(hri); } return hri; } catch (IOException e) { server.abort( "Aborting because error occoured while reading " + Bytes.toStringBinary(regionName) + " from hbase:meta", e); return null; } }
/** * A table is deleted. Remove its regions from all internal maps. We loop through all regions * assuming we don't delete tables too much. */ public void tableDeleted(final TableName tableName) { Set<HRegionInfo> regionsToDelete = new HashSet<HRegionInfo>(); synchronized (this) { for (RegionState state : regionStates.values()) { HRegionInfo region = state.getRegion(); if (region.getTable().equals(tableName)) { regionsToDelete.add(region); } } } for (HRegionInfo region : regionsToDelete) { deleteRegion(region); } }
/** * Gets current state of all regions of the table. This method looks at the in-memory state. It * does not go to <code>hbase:meta</code>. Method guaranteed to return keys for all states in * {@link org.apache.hadoop.hbase.master.RegionState.State} * * @param tableName * @return Online regions from <code>tableName</code> */ public synchronized Map<RegionState.State, List<HRegionInfo>> getRegionByStateOfTable( TableName tableName) { Map<RegionState.State, List<HRegionInfo>> tableRegions = new HashMap<State, List<HRegionInfo>>(); for (State state : State.values()) { tableRegions.put(state, new ArrayList<HRegionInfo>()); } Map<String, RegionState> indexMap = regionStatesTableIndex.get(tableName); if (indexMap == null) return tableRegions; for (RegionState regionState : indexMap.values()) { tableRegions.get(regionState.getState()).add(regionState.getRegion()); } return tableRegions; }
/** * A region is offline, won't be in transition any more. Its state should be the specified * expected state, which can only be Split/Merged/Offline/null(=Offline)/SplittingNew/MergingNew. */ public void regionOffline(final HRegionInfo hri, final State expectedState) { Preconditions.checkArgument( expectedState == null || RegionState.isUnassignable(expectedState), "Offlined region should not be " + expectedState); if (isRegionInState(hri, State.SPLITTING_NEW, State.MERGING_NEW)) { // Remove it from all region maps deleteRegion(hri); return; } State newState = expectedState == null ? State.OFFLINE : expectedState; updateRegionState(hri, newState); String encodedName = hri.getEncodedName(); synchronized (this) { regionsInTransition.remove(encodedName); ServerName oldServerName = regionAssignments.remove(hri); if (oldServerName != null && serverHoldings.containsKey(oldServerName)) { if (newState == State.MERGED || newState == State.SPLIT || hri.isMetaRegion() || tableStateManager.isTableState( hri.getTable(), TableState.State.DISABLED, TableState.State.DISABLING)) { // Offline the region only if it's merged/split, or the table is disabled/disabling. // Otherwise, offline it from this server only when it is online on a different server. LOG.info("Offlined " + hri.getShortNameToLog() + " from " + oldServerName); removeFromServerHoldings(oldServerName, hri); removeFromReplicaMapping(hri); } else { // Need to remember it so that we can offline it from this // server when it is online on a different server. oldAssignments.put(encodedName, oldServerName); } } } }
static boolean isOneOfStates(RegionState regionState, State... states) { State s = regionState != null ? regionState.getState() : null; for (State state : states) { if (s == state) return true; } return false; }
/** * At cluster clean re/start, mark all user regions closed except those of tables that are * excluded, such as disabled/disabling/enabling tables. All user regions and their previous * locations are returned. */ synchronized Map<HRegionInfo, ServerName> closeAllUserRegions(Set<TableName> excludedTables) { boolean noExcludeTables = excludedTables == null || excludedTables.isEmpty(); Set<HRegionInfo> toBeClosed = new HashSet<HRegionInfo>(regionStates.size()); for (RegionState state : regionStates.values()) { HRegionInfo hri = state.getRegion(); if (state.isSplit() || hri.isSplit()) { continue; } TableName tableName = hri.getTable(); if (!TableName.META_TABLE_NAME.equals(tableName) && (noExcludeTables || !excludedTables.contains(tableName))) { toBeClosed.add(hri); } } Map<HRegionInfo, ServerName> allUserRegions = new HashMap<HRegionInfo, ServerName>(toBeClosed.size()); for (HRegionInfo hri : toBeClosed) { RegionState regionState = updateRegionState(hri, State.CLOSED); allUserRegions.put(hri, regionState.getServerName()); } return allUserRegions; }
private RegionState putRegionState(RegionState regionState) { HRegionInfo hri = regionState.getRegion(); String encodedName = hri.getEncodedName(); TableName table = hri.getTable(); RegionState oldState = regionStates.put(encodedName, regionState); Map<String, RegionState> map = regionStatesTableIndex.get(table); if (map == null) { map = new HashMap<String, RegionState>(); regionStatesTableIndex.put(table, map); } map.put(encodedName, regionState); return oldState; }
@Override public int compare(RegionState l, RegionState r) { return Long.compare(l.getStamp(), r.getStamp()); }
/** A server is offline, all regions on it are dead. */ public List<HRegionInfo> serverOffline(final ServerName sn) { // Offline all regions on this server not already in transition. List<HRegionInfo> rits = new ArrayList<HRegionInfo>(); Set<HRegionInfo> regionsToCleanIfNoMetaEntry = new HashSet<HRegionInfo>(); // Offline regions outside the loop and synchronized block to avoid // ConcurrentModificationException and deadlock in case of meta anassigned, // but RegionState a blocked. Set<HRegionInfo> regionsToOffline = new HashSet<HRegionInfo>(); synchronized (this) { Set<HRegionInfo> assignedRegions = serverHoldings.get(sn); if (assignedRegions == null) { assignedRegions = new HashSet<HRegionInfo>(); } for (HRegionInfo region : assignedRegions) { // Offline open regions, no need to offline if SPLIT/MERGED/OFFLINE if (isRegionOnline(region)) { regionsToOffline.add(region); } else if (isRegionInState(region, State.SPLITTING, State.MERGING)) { LOG.debug("Offline splitting/merging region " + getRegionState(region)); regionsToOffline.add(region); } } for (RegionState state : regionsInTransition.values()) { HRegionInfo hri = state.getRegion(); if (assignedRegions.contains(hri)) { // Region is open on this region server, but in transition. // This region must be moving away from this server, or splitting/merging. // SSH will handle it, either skip assigning, or re-assign. LOG.info("Transitioning " + state + " will be handled by ServerCrashProcedure for " + sn); } else if (sn.equals(state.getServerName())) { // Region is in transition on this region server, and this // region is not open on this server. So the region must be // moving to this server from another one (i.e. opening or // pending open on this server, was open on another one. // Offline state is also kind of pending open if the region is in // transition. The region could be in failed_close state too if we have // tried several times to open it while this region server is not reachable) if (isOneOfStates( state, State.OPENING, State.PENDING_OPEN, State.FAILED_OPEN, State.FAILED_CLOSE, State.OFFLINE)) { LOG.info( "Found region in " + state + " to be reassigned by ServerCrashProcedure for " + sn); rits.add(hri); } else if (isOneOfStates(state, State.SPLITTING_NEW)) { regionsToCleanIfNoMetaEntry.add(state.getRegion()); } else { LOG.warn("THIS SHOULD NOT HAPPEN: unexpected " + state); } } } this.notifyAll(); } for (HRegionInfo hri : regionsToOffline) { regionOffline(hri); } cleanIfNoMetaEntry(regionsToCleanIfNoMetaEntry); return rits; }
/** Update a region state. It will be put in transition if not already there. */ public RegionState updateRegionState(final HRegionInfo hri, final State state) { RegionState regionState = getRegionState(hri.getEncodedName()); return updateRegionState(hri, state, regionState == null ? null : regionState.getServerName()); }
/** @return True if hbase:meta table region is in transition. */ public synchronized boolean isMetaRegionInTransition() { for (RegionState state : regionsInTransition.values()) { if (state.getRegion().isMetaRegion()) return true; } return false; }
/** Update a region state. It will be put in transition if not already there. */ private RegionState updateRegionState( final HRegionInfo hri, final RegionState.State state, final ServerName serverName, long openSeqNum) { if (state == RegionState.State.FAILED_CLOSE || state == RegionState.State.FAILED_OPEN) { LOG.warn( "Failed to open/close " + hri.getShortNameToLog() + " on " + serverName + ", set to " + state); } String encodedName = hri.getEncodedName(); RegionState regionState = new RegionState(hri, state, System.currentTimeMillis(), serverName); RegionState oldState = getRegionState(encodedName); if (!regionState.equals(oldState)) { LOG.info("Transition " + oldState + " to " + regionState); // Persist region state before updating in-memory info, if needed regionStateStore.updateRegionState(openSeqNum, regionState, oldState); } synchronized (this) { regionsInTransition.put(encodedName, regionState); putRegionState(regionState); // For these states, region should be properly closed. // There should be no log splitting issue. if ((state == State.CLOSED || state == State.MERGED || state == State.SPLIT) && lastAssignments.containsKey(encodedName)) { ServerName last = lastAssignments.get(encodedName); if (last.equals(serverName)) { lastAssignments.remove(encodedName); } else { LOG.warn(encodedName + " moved to " + state + " on " + serverName + ", expected " + last); } } // Once a region is opened, record its last assignment right away. if (serverName != null && state == State.OPEN) { ServerName last = lastAssignments.get(encodedName); if (!serverName.equals(last)) { lastAssignments.put(encodedName, serverName); if (last != null && isServerDeadAndNotProcessed(last)) { LOG.warn( encodedName + " moved to " + serverName + ", while it's previous host " + last + " is dead but not processed yet"); } } } // notify the change this.notifyAll(); } return regionState; }