http://git-wip-us.apache.org/repos/asf/hbase/blob/a3c5a744/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/MergeTableRegionsProcedure.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/MergeTableRegionsProcedure.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/MergeTableRegionsProcedure.java new file mode 100644 index 0000000..3600fe0 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/MergeTableRegionsProcedure.java @@ -0,0 +1,906 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hbase.master.procedure; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.hbase.DoNotRetryIOException; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.MetaMutationAnnotation; +import org.apache.hadoop.hbase.RegionLoad; +import org.apache.hadoop.hbase.ServerLoad; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.UnknownRegionException; +import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.hbase.client.Mutation; +import org.apache.hadoop.hbase.exceptions.MergeRegionException; +import org.apache.hadoop.hbase.io.hfile.CacheConfig; +import org.apache.hadoop.hbase.master.AssignmentManager; +import org.apache.hadoop.hbase.master.CatalogJanitor; +import org.apache.hadoop.hbase.master.MasterCoprocessorHost; +import org.apache.hadoop.hbase.master.MasterFileSystem; +import org.apache.hadoop.hbase.master.RegionPlan; +import org.apache.hadoop.hbase.master.RegionState; +import org.apache.hadoop.hbase.master.RegionStates; +import org.apache.hadoop.hbase.master.ServerManager; +import org.apache.hadoop.hbase.regionserver.HRegionFileSystem; +import org.apache.hadoop.hbase.regionserver.StoreFile; +import org.apache.hadoop.hbase.regionserver.StoreFileInfo; +import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos; +import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.MergeTableRegionsState; +import org.apache.hadoop.hbase.shaded.protobuf.generated.RegionServerStatusProtos.RegionStateTransition; +import org.apache.hadoop.hbase.shaded.protobuf.generated.RegionServerStatusProtos.RegionStateTransition.TransitionCode; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; +import org.apache.hadoop.hbase.util.FSUtils; + +/** + * The procedure to Merge a region in a table. + */ [email protected] +public class MergeTableRegionsProcedure + extends AbstractStateMachineTableProcedure<MergeTableRegionsState> { + private static final Log LOG = LogFactory.getLog(MergeTableRegionsProcedure.class); + + private Boolean traceEnabled; + private AssignmentManager assignmentManager; + private int timeout; + private ServerName regionLocation; + private String regionsToMergeListFullName; + private String regionsToMergeListEncodedName; + + private HRegionInfo [] regionsToMerge; + private HRegionInfo mergedRegionInfo; + private boolean forcible; + + public MergeTableRegionsProcedure() { + this.traceEnabled = isTraceEnabled(); + this.assignmentManager = null; + this.timeout = -1; + this.regionLocation = null; + this.regionsToMergeListFullName = null; + this.regionsToMergeListEncodedName = null; + } + + public MergeTableRegionsProcedure( + final MasterProcedureEnv env, + final HRegionInfo[] regionsToMerge, + final boolean forcible) throws IOException { + super(env); + this.traceEnabled = isTraceEnabled(); + this.assignmentManager = getAssignmentManager(env); + // For now, we only merge 2 regions. It could be extended to more than 2 regions in + // the future. + assert(regionsToMerge.length == 2); + assert(regionsToMerge[0].getTable() == regionsToMerge[1].getTable()); + this.regionsToMerge = regionsToMerge; + this.forcible = forcible; + + this.timeout = -1; + this.regionsToMergeListFullName = getRegionsToMergeListFullNameString(); + this.regionsToMergeListEncodedName = getRegionsToMergeListEncodedNameString(); + + // Check daughter regions and make sure that we have valid daughter regions before + // doing the real work. + checkDaughterRegions(); + // WARN: make sure there is no parent region of the two merging regions in + // hbase:meta If exists, fixing up daughters would cause daughter regions(we + // have merged one) online again when we restart master, so we should clear + // the parent region to prevent the above case + // Since HBASE-7721, we don't need fix up daughters any more. so here do + // nothing + setupMergedRegionInfo(); + } + + @Override + protected Flow executeFromState( + final MasterProcedureEnv env, + final MergeTableRegionsState state) throws InterruptedException { + if (isTraceEnabled()) { + LOG.trace(this + " execute state=" + state); + } + + try { + switch (state) { + case MERGE_TABLE_REGIONS_PREPARE: + prepareMergeRegion(env); + setNextState(MergeTableRegionsState.MERGE_TABLE_REGIONS_MOVE_REGION_TO_SAME_RS); + break; + case MERGE_TABLE_REGIONS_MOVE_REGION_TO_SAME_RS: + if (MoveRegionsToSameRS(env)) { + setNextState(MergeTableRegionsState.MERGE_TABLE_REGIONS_PRE_MERGE_OPERATION); + } else { + LOG.info("Cancel merging regions " + getRegionsToMergeListFullNameString() + + ", because can't move them to the same RS"); + setNextState(MergeTableRegionsState.MERGE_TABLE_REGIONS_POST_OPERATION); + } + break; + case MERGE_TABLE_REGIONS_PRE_MERGE_OPERATION: + preMergeRegions(env); + setNextState(MergeTableRegionsState.MERGE_TABLE_REGIONS_SET_MERGING_TABLE_STATE); + break; + case MERGE_TABLE_REGIONS_SET_MERGING_TABLE_STATE: + setRegionStateToMerging(env); + setNextState(MergeTableRegionsState.MERGE_TABLE_REGIONS_CLOSE_REGIONS); + break; + case MERGE_TABLE_REGIONS_CLOSE_REGIONS: + closeRegionsForMerge(env); + setNextState(MergeTableRegionsState.MERGE_TABLE_REGIONS_CREATE_MERGED_REGION); + break; + case MERGE_TABLE_REGIONS_CREATE_MERGED_REGION: + createMergedRegion(env); + setNextState(MergeTableRegionsState.MERGE_TABLE_REGIONS_PRE_MERGE_COMMIT_OPERATION); + break; + case MERGE_TABLE_REGIONS_PRE_MERGE_COMMIT_OPERATION: + preMergeRegionsCommit(env); + setNextState(MergeTableRegionsState.MERGE_TABLE_REGIONS_UPDATE_META); + break; + case MERGE_TABLE_REGIONS_UPDATE_META: + updateMetaForMergedRegions(env); + setNextState(MergeTableRegionsState.MERGE_TABLE_REGIONS_POST_MERGE_COMMIT_OPERATION); + break; + case MERGE_TABLE_REGIONS_POST_MERGE_COMMIT_OPERATION: + postMergeRegionsCommit(env); + setNextState(MergeTableRegionsState.MERGE_TABLE_REGIONS_OPEN_MERGED_REGION); + break; + case MERGE_TABLE_REGIONS_OPEN_MERGED_REGION: + openMergedRegions(env); + setNextState(MergeTableRegionsState.MERGE_TABLE_REGIONS_POST_OPERATION); + break; + case MERGE_TABLE_REGIONS_POST_OPERATION: + postCompletedMergeRegions(env); + return Flow.NO_MORE_STATE; + default: + throw new UnsupportedOperationException(this + " unhandled state=" + state); + } + } catch (IOException e) { + LOG.warn("Error trying to merge regions " + getRegionsToMergeListFullNameString() + + " in the table " + getTableName() + " (in state=" + state + ")", e); + + setFailure("master-merge-regions", e); + } + return Flow.HAS_MORE_STATE; + } + + @Override + protected void rollbackState( + final MasterProcedureEnv env, + final MergeTableRegionsState state) throws IOException, InterruptedException { + if (isTraceEnabled()) { + LOG.trace(this + " rollback state=" + state); + } + + try { + switch (state) { + case MERGE_TABLE_REGIONS_POST_OPERATION: + case MERGE_TABLE_REGIONS_OPEN_MERGED_REGION: + case MERGE_TABLE_REGIONS_POST_MERGE_COMMIT_OPERATION: + case MERGE_TABLE_REGIONS_UPDATE_META: + String msg = this + " We are in the " + state + " state." + + " It is complicated to rollback the merge operation that region server is working on." + + " Rollback is not supported and we should let the merge operation to complete"; + LOG.warn(msg); + // PONR + throw new UnsupportedOperationException(this + " unhandled state=" + state); + case MERGE_TABLE_REGIONS_PRE_MERGE_COMMIT_OPERATION: + break; + case MERGE_TABLE_REGIONS_CREATE_MERGED_REGION: + cleanupMergedRegion(env); + break; + case MERGE_TABLE_REGIONS_CLOSE_REGIONS: + rollbackCloseRegionsForMerge(env); + break; + case MERGE_TABLE_REGIONS_SET_MERGING_TABLE_STATE: + setRegionStateToRevertMerging(env); + break; + case MERGE_TABLE_REGIONS_PRE_MERGE_OPERATION: + postRollBackMergeRegions(env); + break; + case MERGE_TABLE_REGIONS_MOVE_REGION_TO_SAME_RS: + break; // nothing to rollback + case MERGE_TABLE_REGIONS_PREPARE: + break; // nothing to rollback + default: + throw new UnsupportedOperationException(this + " unhandled state=" + state); + } + } catch (Exception e) { + // This will be retried. Unless there is a bug in the code, + // this should be just a "temporary error" (e.g. network down) + LOG.warn("Failed rollback attempt step " + state + " for merging the regions " + + getRegionsToMergeListFullNameString() + " in table " + getTableName(), e); + throw e; + } + } + + /* + * Check whether we are in the state that can be rollback + */ + @Override + protected boolean isRollbackSupported(final MergeTableRegionsState state) { + switch (state) { + case MERGE_TABLE_REGIONS_POST_OPERATION: + case MERGE_TABLE_REGIONS_OPEN_MERGED_REGION: + case MERGE_TABLE_REGIONS_POST_MERGE_COMMIT_OPERATION: + case MERGE_TABLE_REGIONS_UPDATE_META: + // It is not safe to rollback if we reach to these states. + return false; + default: + break; + } + return true; + } + + @Override + protected MergeTableRegionsState getState(final int stateId) { + return MergeTableRegionsState.forNumber(stateId); + } + + @Override + protected int getStateId(final MergeTableRegionsState state) { + return state.getNumber(); + } + + @Override + protected MergeTableRegionsState getInitialState() { + return MergeTableRegionsState.MERGE_TABLE_REGIONS_PREPARE; + } + + @Override + public void serializeStateData(final OutputStream stream) throws IOException { + super.serializeStateData(stream); + + MasterProcedureProtos.MergeTableRegionsStateData.Builder mergeTableRegionsMsg = + MasterProcedureProtos.MergeTableRegionsStateData.newBuilder() + .setUserInfo(MasterProcedureUtil.toProtoUserInfo(getUser())) + .setMergedRegionInfo(HRegionInfo.convert(mergedRegionInfo)) + .setForcible(forcible); + for (HRegionInfo hri: regionsToMerge) { + mergeTableRegionsMsg.addRegionInfo(HRegionInfo.convert(hri)); + } + mergeTableRegionsMsg.build().writeDelimitedTo(stream); + } + + @Override + public void deserializeStateData(final InputStream stream) throws IOException { + super.deserializeStateData(stream); + + MasterProcedureProtos.MergeTableRegionsStateData mergeTableRegionsMsg = + MasterProcedureProtos.MergeTableRegionsStateData.parseDelimitedFrom(stream); + setUser(MasterProcedureUtil.toUserInfo(mergeTableRegionsMsg.getUserInfo())); + + assert(mergeTableRegionsMsg.getRegionInfoCount() == 2); + regionsToMerge = new HRegionInfo[mergeTableRegionsMsg.getRegionInfoCount()]; + for (int i = 0; i < regionsToMerge.length; i++) { + regionsToMerge[i] = HRegionInfo.convert(mergeTableRegionsMsg.getRegionInfo(i)); + } + + mergedRegionInfo = HRegionInfo.convert(mergeTableRegionsMsg.getMergedRegionInfo()); + } + + @Override + public void toStringClassDetails(StringBuilder sb) { + sb.append(getClass().getSimpleName()); + sb.append(" (table="); + sb.append(getTableName()); + sb.append(" regions="); + sb.append(getRegionsToMergeListFullNameString()); + sb.append(" forcible="); + sb.append(forcible); + sb.append(")"); + } + + @Override + protected LockState acquireLock(final MasterProcedureEnv env) { + if (env.waitInitialized(this)) { + return LockState.LOCK_EVENT_WAIT; + } + return env.getProcedureScheduler().waitRegions(this, getTableName(), + regionsToMerge[0], regionsToMerge[1])? + LockState.LOCK_EVENT_WAIT: LockState.LOCK_ACQUIRED; + } + + @Override + protected void releaseLock(final MasterProcedureEnv env) { + env.getProcedureScheduler().wakeRegions(this, getTableName(), + regionsToMerge[0], regionsToMerge[1]); + } + + @Override + public TableName getTableName() { + return regionsToMerge[0].getTable(); + } + + @Override + public TableOperationType getTableOperationType() { + return TableOperationType.MERGE; + } + + /** + * check daughter regions + * @throws IOException + */ + private void checkDaughterRegions() throws IOException { + // Note: the following logic assumes that we only have 2 regions to merge. In the future, + // if we want to extend to more than 2 regions, the code needs to modify a little bit. + // + if (regionsToMerge[0].getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID || + regionsToMerge[1].getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID) { + throw new MergeRegionException("Can't merge non-default replicas"); + } + + if (!HRegionInfo.areAdjacent(regionsToMerge[0], regionsToMerge[1])) { + String msg = "Trying to merge non-adjacent regions " + + getRegionsToMergeListFullNameString() + " where forcible = " + forcible; + LOG.warn(msg); + if (!forcible) { + throw new DoNotRetryIOException(msg); + } + } + } + + /** + * Prepare merge and do some check + * @param env MasterProcedureEnv + * @throws IOException + */ + private void prepareMergeRegion(final MasterProcedureEnv env) throws IOException { + // Note: the following logic assumes that we only have 2 regions to merge. In the future, + // if we want to extend to more than 2 regions, the code needs to modify a little bit. + // + CatalogJanitor catalogJanitor = env.getMasterServices().getCatalogJanitor(); + boolean regionAHasMergeQualifier = !catalogJanitor.cleanMergeQualifier(regionsToMerge[0]); + if (regionAHasMergeQualifier + || !catalogJanitor.cleanMergeQualifier(regionsToMerge[1])) { + String msg = "Skip merging regions " + getRegionsToMergeListFullNameString() + + ", because region " + + (regionAHasMergeQualifier ? regionsToMerge[0].getEncodedName() : regionsToMerge[1] + .getEncodedName()) + " has merge qualifier"; + LOG.warn(msg); + throw new MergeRegionException(msg); + } + + RegionStates regionStates = getAssignmentManager(env).getRegionStates(); + RegionState regionStateA = regionStates.getRegionState(regionsToMerge[0].getEncodedName()); + RegionState regionStateB = regionStates.getRegionState(regionsToMerge[1].getEncodedName()); + if (regionStateA == null || regionStateB == null) { + throw new UnknownRegionException( + regionStateA == null ? + regionsToMerge[0].getEncodedName() : regionsToMerge[1].getEncodedName()); + } + + if (!regionStateA.isOpened() || !regionStateB.isOpened()) { + throw new MergeRegionException( + "Unable to merge regions not online " + regionStateA + ", " + regionStateB); + } + } + + /** + * Create merged region info through the specified two regions + */ + private void setupMergedRegionInfo() { + long rid = EnvironmentEdgeManager.currentTime(); + // Regionid is timestamp. Merged region's id can't be less than that of + // merging regions else will insert at wrong location in hbase:meta + if (rid < regionsToMerge[0].getRegionId() || rid < regionsToMerge[1].getRegionId()) { + LOG.warn("Clock skew; merging regions id are " + regionsToMerge[0].getRegionId() + + " and " + regionsToMerge[1].getRegionId() + ", but current time here is " + rid); + rid = Math.max(regionsToMerge[0].getRegionId(), regionsToMerge[1].getRegionId()) + 1; + } + + byte[] startKey = null; + byte[] endKey = null; + // Choose the smaller as start key + if (regionsToMerge[0].compareTo(regionsToMerge[1]) <= 0) { + startKey = regionsToMerge[0].getStartKey(); + } else { + startKey = regionsToMerge[1].getStartKey(); + } + // Choose the bigger as end key + if (Bytes.equals(regionsToMerge[0].getEndKey(), HConstants.EMPTY_BYTE_ARRAY) + || (!Bytes.equals(regionsToMerge[1].getEndKey(), HConstants.EMPTY_BYTE_ARRAY) + && Bytes.compareTo(regionsToMerge[0].getEndKey(), regionsToMerge[1].getEndKey()) > 0)) { + endKey = regionsToMerge[0].getEndKey(); + } else { + endKey = regionsToMerge[1].getEndKey(); + } + + // Merged region is sorted between two merging regions in META + mergedRegionInfo = new HRegionInfo(getTableName(), startKey, endKey, false, rid); + } + + /** + * Move all regions to the same region server + * @param env MasterProcedureEnv + * @return whether target regions hosted by the same RS + * @throws IOException + */ + private boolean MoveRegionsToSameRS(final MasterProcedureEnv env) throws IOException { + // Make sure regions are on the same regionserver before send merge + // regions request to region server. + // + boolean onSameRS = isRegionsOnTheSameServer(env); + if (!onSameRS) { + // Note: the following logic assumes that we only have 2 regions to merge. In the future, + // if we want to extend to more than 2 regions, the code needs to modify a little bit. + // + RegionStates regionStates = getAssignmentManager(env).getRegionStates(); + ServerName regionLocation2 = regionStates.getRegionServerOfRegion(regionsToMerge[1]); + + RegionLoad loadOfRegionA = getRegionLoad(env, regionLocation, regionsToMerge[0]); + RegionLoad loadOfRegionB = getRegionLoad(env, regionLocation2, regionsToMerge[1]); + if (loadOfRegionA != null && loadOfRegionB != null + && loadOfRegionA.getRequestsCount() < loadOfRegionB.getRequestsCount()) { + // switch regionsToMerge[0] and regionsToMerge[1] + HRegionInfo tmpRegion = this.regionsToMerge[0]; + this.regionsToMerge[0] = this.regionsToMerge[1]; + this.regionsToMerge[1] = tmpRegion; + ServerName tmpLocation = regionLocation; + regionLocation = regionLocation2; + regionLocation2 = tmpLocation; + } + + long startTime = EnvironmentEdgeManager.currentTime(); + + RegionPlan regionPlan = new RegionPlan(regionsToMerge[1], regionLocation2, regionLocation); + LOG.info("Moving regions to same server for merge: " + regionPlan.toString()); + getAssignmentManager(env).balance(regionPlan); + do { + try { + Thread.sleep(20); + // Make sure check RIT first, then get region location, otherwise + // we would make a wrong result if region is online between getting + // region location and checking RIT + boolean isRIT = regionStates.isRegionInTransition(regionsToMerge[1]); + regionLocation2 = regionStates.getRegionServerOfRegion(regionsToMerge[1]); + onSameRS = regionLocation.equals(regionLocation2); + if (onSameRS || !isRIT) { + // Regions are on the same RS, or regionsToMerge[1] is not in + // RegionInTransition any more + break; + } + } catch (InterruptedException e) { + InterruptedIOException iioe = new InterruptedIOException(); + iioe.initCause(e); + throw iioe; + } + } while ((EnvironmentEdgeManager.currentTime() - startTime) <= getTimeout(env)); + } + return onSameRS; + } + + /** + * Pre merge region action + * @param env MasterProcedureEnv + **/ + private void preMergeRegions(final MasterProcedureEnv env) throws IOException { + final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost(); + if (cpHost != null) { + boolean ret = cpHost.preMergeRegionsAction(regionsToMerge, getUser()); + if (ret) { + throw new IOException( + "Coprocessor bypassing regions " + getRegionsToMergeListFullNameString() + " merge."); + } + } + } + + /** + * Action after rollback a merge table regions action. + * @param env MasterProcedureEnv + * @throws IOException + */ + private void postRollBackMergeRegions(final MasterProcedureEnv env) throws IOException { + final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost(); + if (cpHost != null) { + cpHost.postRollBackMergeRegionsAction(regionsToMerge, getUser()); + } + } + + /** + * Set the region states to MERGING state + * @param env MasterProcedureEnv + * @throws IOException + */ + public void setRegionStateToMerging(final MasterProcedureEnv env) throws IOException { + RegionStateTransition.Builder transition = RegionStateTransition.newBuilder(); + transition.setTransitionCode(TransitionCode.READY_TO_MERGE); + transition.addRegionInfo(HRegionInfo.convert(mergedRegionInfo)); + transition.addRegionInfo(HRegionInfo.convert(regionsToMerge[0])); + transition.addRegionInfo(HRegionInfo.convert(regionsToMerge[1])); + if (env.getMasterServices().getAssignmentManager().onRegionTransition( + getServerName(env), transition.build()) != null) { + throw new IOException("Failed to update region state to MERGING for " + + getRegionsToMergeListFullNameString()); + } + } + + /** + * Rollback the region state change + * @param env MasterProcedureEnv + * @throws IOException + */ + private void setRegionStateToRevertMerging(final MasterProcedureEnv env) throws IOException { + RegionStateTransition.Builder transition = RegionStateTransition.newBuilder(); + transition.setTransitionCode(TransitionCode.MERGE_REVERTED); + transition.addRegionInfo(HRegionInfo.convert(mergedRegionInfo)); + transition.addRegionInfo(HRegionInfo.convert(regionsToMerge[0])); + transition.addRegionInfo(HRegionInfo.convert(regionsToMerge[1])); + String msg = env.getMasterServices().getAssignmentManager().onRegionTransition( + getServerName(env), transition.build()); + if (msg != null) { + // If daughter regions are online, the msg is coming from RPC retry. Ignore it. + RegionStates regionStates = getAssignmentManager(env).getRegionStates(); + if (!regionStates.isRegionOnline(regionsToMerge[0]) || + !regionStates.isRegionOnline(regionsToMerge[1])) { + throw new IOException("Failed to update region state for " + + getRegionsToMergeListFullNameString() + + " as part of operation for reverting merge. Error message: " + msg); + } + } + } + + /** + * Create merged region + * @param env MasterProcedureEnv + * @throws IOException + */ + private void createMergedRegion(final MasterProcedureEnv env) throws IOException { + final MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem(); + final Path tabledir = FSUtils.getTableDir(mfs.getRootDir(), regionsToMerge[0].getTable()); + final FileSystem fs = mfs.getFileSystem(); + HRegionFileSystem regionFs = HRegionFileSystem.openRegionFromFileSystem( + env.getMasterConfiguration(), fs, tabledir, regionsToMerge[0], false); + regionFs.createMergesDir(); + + mergeStoreFiles(env, regionFs, regionFs.getMergesDir()); + HRegionFileSystem regionFs2 = HRegionFileSystem.openRegionFromFileSystem( + env.getMasterConfiguration(), fs, tabledir, regionsToMerge[1], false); + mergeStoreFiles(env, regionFs2, regionFs.getMergesDir()); + + regionFs.commitMergedRegion(mergedRegionInfo); + } + + /** + * Create reference file(s) of merging regions under the merges directory + * @param env MasterProcedureEnv + * @param regionFs region file system + * @param mergedDir the temp directory of merged region + * @throws IOException + */ + private void mergeStoreFiles( + final MasterProcedureEnv env, final HRegionFileSystem regionFs, final Path mergedDir) + throws IOException { + final MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem(); + final Configuration conf = env.getMasterConfiguration(); + final HTableDescriptor htd = env.getMasterServices().getTableDescriptors().get(getTableName()); + + for (String family: regionFs.getFamilies()) { + final HColumnDescriptor hcd = htd.getFamily(family.getBytes()); + final Collection<StoreFileInfo> storeFiles = regionFs.getStoreFiles(family); + + if (storeFiles != null && storeFiles.size() > 0) { + final CacheConfig cacheConf = new CacheConfig(conf, hcd); + for (StoreFileInfo storeFileInfo: storeFiles) { + // Create reference file(s) of the region in mergedDir + regionFs.mergeStoreFile(mergedRegionInfo, family, new StoreFile(mfs.getFileSystem(), + storeFileInfo, conf, cacheConf, hcd.getBloomFilterType(), true), + mergedDir); + } + } + } + } + + /** + * Clean up merged region + * @param env MasterProcedureEnv + * @throws IOException + */ + private void cleanupMergedRegion(final MasterProcedureEnv env) throws IOException { + final MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem(); + final Path tabledir = FSUtils.getTableDir(mfs.getRootDir(), regionsToMerge[0].getTable()); + final FileSystem fs = mfs.getFileSystem(); + HRegionFileSystem regionFs = HRegionFileSystem.openRegionFromFileSystem( + env.getMasterConfiguration(), fs, tabledir, regionsToMerge[0], false); + regionFs.cleanupMergedRegion(mergedRegionInfo); + } + + /** + * RPC to region server that host the regions to merge, ask for close these regions + * @param env MasterProcedureEnv + * @throws IOException + */ + private void closeRegionsForMerge(final MasterProcedureEnv env) throws IOException { + boolean success = env.getMasterServices().getServerManager().sendRegionCloseForSplitOrMerge( + getServerName(env), regionsToMerge[0], regionsToMerge[1]); + if (!success) { + throw new IOException("Close regions " + getRegionsToMergeListFullNameString() + + " for merging failed. Check region server log for more details."); + } + } + + /** + * Rollback close regions + * @param env MasterProcedureEnv + **/ + private void rollbackCloseRegionsForMerge(final MasterProcedureEnv env) throws IOException { + // Check whether the region is closed; if so, open it in the same server + RegionStates regionStates = getAssignmentManager(env).getRegionStates(); + for(int i = 1; i < regionsToMerge.length; i++) { + RegionState state = regionStates.getRegionState(regionsToMerge[i]); + if (state != null && (state.isClosing() || state.isClosed())) { + env.getMasterServices().getServerManager().sendRegionOpen( + getServerName(env), + regionsToMerge[i], + ServerName.EMPTY_SERVER_LIST); + } + } + } + + /** + * Post merge region action + * @param env MasterProcedureEnv + **/ + private void preMergeRegionsCommit(final MasterProcedureEnv env) throws IOException { + final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost(); + if (cpHost != null) { + @MetaMutationAnnotation + final List<Mutation> metaEntries = new ArrayList<>(); + boolean ret = cpHost.preMergeRegionsCommit(regionsToMerge, metaEntries, getUser()); + + if (ret) { + throw new IOException( + "Coprocessor bypassing regions " + getRegionsToMergeListFullNameString() + " merge."); + } + try { + for (Mutation p : metaEntries) { + HRegionInfo.parseRegionName(p.getRow()); + } + } catch (IOException e) { + LOG.error("Row key of mutation from coprocessor is not parsable as region name." + + "Mutations from coprocessor should only be for hbase:meta table.", e); + throw e; + } + } + } + + /** + * Add merged region to META and delete original regions. + * @param env MasterProcedureEnv + * @throws IOException + */ + private void updateMetaForMergedRegions(final MasterProcedureEnv env) throws IOException { + RegionStateTransition.Builder transition = RegionStateTransition.newBuilder(); + transition.setTransitionCode(TransitionCode.MERGE_PONR); + transition.addRegionInfo(HRegionInfo.convert(mergedRegionInfo)); + transition.addRegionInfo(HRegionInfo.convert(regionsToMerge[0])); + transition.addRegionInfo(HRegionInfo.convert(regionsToMerge[1])); + // Add merged region and delete original regions + // as an atomic update. See HBASE-7721. This update to hbase:meta makes the region + // will determine whether the region is merged or not in case of failures. + if (env.getMasterServices().getAssignmentManager().onRegionTransition( + getServerName(env), transition.build()) != null) { + throw new IOException("Failed to update meta to add merged region that merges " + + getRegionsToMergeListFullNameString()); + } + } + + /** + * Post merge region action + * @param env MasterProcedureEnv + **/ + private void postMergeRegionsCommit(final MasterProcedureEnv env) throws IOException { + final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost(); + if (cpHost != null) { + cpHost.postMergeRegionsCommit(regionsToMerge, mergedRegionInfo, getUser()); + } + } + + /** + * Assign merged region + * @param env MasterProcedureEnv + * @throws IOException + * @throws InterruptedException + **/ + private void openMergedRegions(final MasterProcedureEnv env) + throws IOException, InterruptedException { + // Check whether the merged region is already opened; if so, + // this is retry and we should just ignore. + RegionState regionState = + getAssignmentManager(env).getRegionStates().getRegionState(mergedRegionInfo); + if (regionState != null && regionState.isOpened()) { + LOG.info("Skip opening merged region " + mergedRegionInfo.getRegionNameAsString() + + " as it is already opened."); + return; + } + + // TODO: The new AM should provide an API to force assign the merged region to the same RS + // as daughter regions; if the RS is unavailable, then assign to a different RS. + env.getMasterServices().getAssignmentManager().assignMergedRegion( + mergedRegionInfo, regionsToMerge[0], regionsToMerge[1]); + } + + /** + * Post merge region action + * @param env MasterProcedureEnv + **/ + private void postCompletedMergeRegions(final MasterProcedureEnv env) throws IOException { + final MasterCoprocessorHost cpHost = env.getMasterCoprocessorHost(); + if (cpHost != null) { + cpHost.postCompletedMergeRegionsAction(regionsToMerge, mergedRegionInfo, getUser()); + } + } + + private RegionLoad getRegionLoad( + final MasterProcedureEnv env, + final ServerName sn, + final HRegionInfo hri) { + ServerManager serverManager = env.getMasterServices().getServerManager(); + ServerLoad load = serverManager.getLoad(sn); + if (load != null) { + Map<byte[], RegionLoad> regionsLoad = load.getRegionsLoad(); + if (regionsLoad != null) { + return regionsLoad.get(hri.getRegionName()); + } + } + return null; + } + + /** + * The procedure could be restarted from a different machine. If the variable is null, we need to + * retrieve it. + * @param env MasterProcedureEnv + * @return whether target regions hosted by the same RS + */ + private boolean isRegionsOnTheSameServer(final MasterProcedureEnv env) throws IOException{ + Boolean onSameRS = true; + int i = 0; + RegionStates regionStates = getAssignmentManager(env).getRegionStates(); + regionLocation = regionStates.getRegionServerOfRegion(regionsToMerge[i]); + if (regionLocation != null) { + for(i = 1; i < regionsToMerge.length; i++) { + ServerName regionLocation2 = regionStates.getRegionServerOfRegion(regionsToMerge[i]); + if (regionLocation2 != null) { + if (onSameRS) { + onSameRS = regionLocation.equals(regionLocation2); + } + } else { + // At least one region is not online, merge will fail, no need to continue. + break; + } + } + if (i == regionsToMerge.length) { + // Finish checking all regions, return the result; + return onSameRS; + } + } + + // If reaching here, at least one region is not online. + String msg = "Skip merging regions " + getRegionsToMergeListFullNameString() + + ", because region " + regionsToMerge[i].getEncodedName() + " is not online now."; + LOG.warn(msg); + throw new IOException(msg); + } + + /** + * The procedure could be restarted from a different machine. If the variable is null, we need to + * retrieve it. + * @param env MasterProcedureEnv + * @return assignmentManager + */ + private AssignmentManager getAssignmentManager(final MasterProcedureEnv env) { + if (assignmentManager == null) { + assignmentManager = env.getMasterServices().getAssignmentManager(); + } + return assignmentManager; + } + + /** + * The procedure could be restarted from a different machine. If the variable is null, we need to + * retrieve it. + * @param env MasterProcedureEnv + * @return timeout value + */ + private int getTimeout(final MasterProcedureEnv env) { + if (timeout == -1) { + timeout = env.getMasterConfiguration().getInt( + "hbase.master.regionmerge.timeout", regionsToMerge.length * 60 * 1000); + } + return timeout; + } + + /** + * The procedure could be restarted from a different machine. If the variable is null, we need to + * retrieve it. + * @param env MasterProcedureEnv + * @return serverName + */ + private ServerName getServerName(final MasterProcedureEnv env) { + if (regionLocation == null) { + regionLocation = + getAssignmentManager(env).getRegionStates().getRegionServerOfRegion(regionsToMerge[0]); + } + return regionLocation; + } + + /** + * The procedure could be restarted from a different machine. If the variable is null, we need to + * retrieve it. + * @param fullName whether return only encoded name + * @return region names in a list + */ + private String getRegionsToMergeListFullNameString() { + if (regionsToMergeListFullName == null) { + StringBuilder sb = new StringBuilder("["); + int i = 0; + while(i < regionsToMerge.length - 1) { + sb.append(regionsToMerge[i].getRegionNameAsString() + ", "); + i++; + } + sb.append(regionsToMerge[i].getRegionNameAsString() + " ]"); + regionsToMergeListFullName = sb.toString(); + } + return regionsToMergeListFullName; + } + + /** + * The procedure could be restarted from a different machine. If the variable is null, we need to + * retrieve it. + * @return encoded region names + */ + private String getRegionsToMergeListEncodedNameString() { + if (regionsToMergeListEncodedName == null) { + StringBuilder sb = new StringBuilder("["); + int i = 0; + while(i < regionsToMerge.length - 1) { + sb.append(regionsToMerge[i].getEncodedName() + ", "); + i++; + } + sb.append(regionsToMerge[i].getEncodedName() + " ]"); + regionsToMergeListEncodedName = sb.toString(); + } + return regionsToMergeListEncodedName; + } + + /** + * The procedure could be restarted from a different machine. If the variable is null, we need to + * retrieve it. + * @return traceEnabled + */ + private Boolean isTraceEnabled() { + if (traceEnabled == null) { + traceEnabled = LOG.isTraceEnabled(); + } + return traceEnabled; + } +}
http://git-wip-us.apache.org/repos/asf/hbase/blob/a3c5a744/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyColumnFamilyProcedure.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyColumnFamilyProcedure.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyColumnFamilyProcedure.java index 622c19f..52bb4d5 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyColumnFamilyProcedure.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyColumnFamilyProcedure.java @@ -21,14 +21,17 @@ package org.apache.hadoop.hbase.master.procedure; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.InvalidFamilyOperationException; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.hbase.client.TableState; import org.apache.hadoop.hbase.master.MasterCoprocessorHost; import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos; @@ -94,9 +97,7 @@ public class ModifyColumnFamilyProcedure setNextState(ModifyColumnFamilyState.MODIFY_COLUMN_FAMILY_REOPEN_ALL_REGIONS); break; case MODIFY_COLUMN_FAMILY_REOPEN_ALL_REGIONS: - if (env.getAssignmentManager().isTableEnabled(getTableName())) { - addChildProcedure(env.getAssignmentManager().createReopenProcedures(getTableName())); - } + reOpenAllRegionsIfTableIsOnline(env); return Flow.NO_MORE_STATE; default: throw new UnsupportedOperationException(this + " unhandled state=" + state); @@ -264,8 +265,7 @@ public class ModifyColumnFamilyProcedure env.getMasterServices().getTableDescriptors().add(unmodifiedHTableDescriptor); // Make sure regions are opened after table descriptor is updated. - //reOpenAllRegionsIfTableIsOnline(env); - // TODO: NUKE ROLLBACK!!!! + reOpenAllRegionsIfTableIsOnline(env); } /** @@ -281,6 +281,26 @@ public class ModifyColumnFamilyProcedure } /** + * Last action from the procedure - executed when online schema change is supported. + * @param env MasterProcedureEnv + * @throws IOException + */ + private void reOpenAllRegionsIfTableIsOnline(final MasterProcedureEnv env) throws IOException { + // This operation only run when the table is enabled. + if (!env.getMasterServices().getTableStateManager() + .isTableState(getTableName(), TableState.State.ENABLED)) { + return; + } + + List<HRegionInfo> regionInfoList = ProcedureSyncWait.getRegionsFromMeta(env, getTableName()); + if (MasterDDLOperationHelper.reOpenAllRegions(env, getTableName(), regionInfoList)) { + LOG.info("Completed add column family operation on table " + getTableName()); + } else { + LOG.warn("Error on reopening the regions on table " + getTableName()); + } + } + + /** * The procedure could be restarted from a different machine. If the variable is null, we need to * retrieve it. * @return traceEnabled http://git-wip-us.apache.org/repos/asf/hbase/blob/a3c5a744/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyTableProcedure.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyTableProcedure.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyTableProcedure.java index 20a6a03..6a70f62 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyTableProcedure.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyTableProcedure.java @@ -120,10 +120,7 @@ public class ModifyTableProcedure setNextState(ModifyTableState.MODIFY_TABLE_REOPEN_ALL_REGIONS); break; case MODIFY_TABLE_REOPEN_ALL_REGIONS: - if (env.getAssignmentManager().isTableEnabled(getTableName())) { - addChildProcedure(env.getAssignmentManager() - .createReopenProcedures(getRegionInfoList(env))); - } + reOpenAllRegionsIfTableIsOnline(env); return Flow.NO_MORE_STATE; default: throw new UnsupportedOperationException("unhandled state=" + state); @@ -302,8 +299,7 @@ public class ModifyTableProcedure deleteFromFs(env, modifiedHTableDescriptor, unmodifiedHTableDescriptor); // Make sure regions are opened after table descriptor is updated. - //reOpenAllRegionsIfTableIsOnline(env); - // TODO: NUKE ROLLBACK!!!! + reOpenAllRegionsIfTableIsOnline(env); } /** @@ -378,6 +374,25 @@ public class ModifyTableProcedure } /** + * Last action from the procedure - executed when online schema change is supported. + * @param env MasterProcedureEnv + * @throws IOException + */ + private void reOpenAllRegionsIfTableIsOnline(final MasterProcedureEnv env) throws IOException { + // This operation only run when the table is enabled. + if (!env.getMasterServices().getTableStateManager() + .isTableState(getTableName(), TableState.State.ENABLED)) { + return; + } + + if (MasterDDLOperationHelper.reOpenAllRegions(env, getTableName(), getRegionInfoList(env))) { + LOG.info("Completed modify table operation on table " + getTableName()); + } else { + LOG.warn("Error on reopening the regions on table " + getTableName()); + } + } + + /** * The procedure could be restarted from a different machine. If the variable is null, we need to * retrieve it. * @return traceEnabled whether the trace is enabled @@ -415,8 +430,7 @@ public class ModifyTableProcedure private List<HRegionInfo> getRegionInfoList(final MasterProcedureEnv env) throws IOException { if (regionInfoList == null) { - regionInfoList = env.getAssignmentManager().getRegionStates() - .getRegionsOfTable(getTableName()); + regionInfoList = ProcedureSyncWait.getRegionsFromMeta(env, getTableName()); } return regionInfoList; } http://git-wip-us.apache.org/repos/asf/hbase/blob/a3c5a744/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ProcedureSyncWait.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ProcedureSyncWait.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ProcedureSyncWait.java index 5199bf8..3777c79 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ProcedureSyncWait.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ProcedureSyncWait.java @@ -21,26 +21,30 @@ package org.apache.hadoop.hbase.master.procedure; import java.io.IOException; import java.io.InterruptedIOException; import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.CoordinatedStateException; import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.MetaTableAccessor; import org.apache.hadoop.hbase.NotAllMetaRegionsOnlineException; import org.apache.hadoop.hbase.ProcedureInfo; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.classification.InterfaceStability; import org.apache.hadoop.hbase.exceptions.TimeoutIOException; -import org.apache.hadoop.hbase.master.assignment.RegionStates; +import org.apache.hadoop.hbase.master.AssignmentManager; +import org.apache.hadoop.hbase.master.RegionState.State; +import org.apache.hadoop.hbase.master.RegionStates; +import org.apache.hadoop.hbase.master.ServerManager; import org.apache.hadoop.hbase.procedure2.Procedure; import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; import org.apache.hadoop.hbase.quotas.MasterQuotaManager; import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; +import org.apache.hadoop.hbase.util.Threads; +import org.apache.hadoop.hbase.zookeeper.MetaTableLocator; /** * Helper to synchronously wait on conditions. @@ -60,93 +64,19 @@ public final class ProcedureSyncWait { T evaluate() throws IOException; } - private static class ProcedureFuture implements Future<byte[]> { - private final ProcedureExecutor<MasterProcedureEnv> procExec; - private final long procId; - - private boolean hasResult = false; - private byte[] result = null; - - public ProcedureFuture(ProcedureExecutor<MasterProcedureEnv> procExec, long procId) { - this.procExec = procExec; - this.procId = procId; - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { return false; } - - @Override - public boolean isCancelled() { return false; } - - @Override - public boolean isDone() { return hasResult; } - - @Override - public byte[] get() throws InterruptedException, ExecutionException { - if (hasResult) return result; - try { - return waitForProcedureToComplete(procExec, procId, Long.MAX_VALUE); - } catch (Exception e) { - throw new ExecutionException(e); - } - } - - @Override - public byte[] get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - if (hasResult) return result; - try { - result = waitForProcedureToComplete(procExec, procId, unit.toMillis(timeout)); - hasResult = true; - return result; - } catch (TimeoutIOException e) { - throw new TimeoutException(e.getMessage()); - } catch (Exception e) { - throw new ExecutionException(e); - } - } - } - - public static Future<byte[]> submitProcedure(final ProcedureExecutor<MasterProcedureEnv> procExec, - final Procedure proc) { - if (proc.isInitializing()) { - procExec.submitProcedure(proc); - } - return new ProcedureFuture(procExec, proc.getProcId()); - } - public static byte[] submitAndWaitProcedure(ProcedureExecutor<MasterProcedureEnv> procExec, final Procedure proc) throws IOException { - if (proc.isInitializing()) { - procExec.submitProcedure(proc); - } - return waitForProcedureToCompleteIOE(procExec, proc.getProcId(), Long.MAX_VALUE); + long procId = procExec.submitProcedure(proc); + return waitForProcedureToComplete(procExec, procId); } - public static byte[] waitForProcedureToCompleteIOE( - final ProcedureExecutor<MasterProcedureEnv> procExec, final long procId, final long timeout) - throws IOException { - try { - return waitForProcedureToComplete(procExec, procId, timeout); - } catch (IOException e) { - throw e; - } catch (Exception e) { - throw new IOException(e); + private static byte[] waitForProcedureToComplete(ProcedureExecutor<MasterProcedureEnv> procExec, + final long procId) throws IOException { + while (!procExec.isFinished(procId) && procExec.isRunning()) { + // TODO: add a config to make it tunable + // Dev Consideration: are we waiting forever, or we can set up some timeout value? + Threads.sleepWithoutInterrupt(250); } - } - - public static byte[] waitForProcedureToComplete( - final ProcedureExecutor<MasterProcedureEnv> procExec, final long procId, final long timeout) - throws IOException { - waitFor(procExec.getEnvironment(), "pid=" + procId, - new ProcedureSyncWait.Predicate<Boolean>() { - @Override - public Boolean evaluate() throws IOException { - return !procExec.isRunning() || procExec.isFinished(procId); - } - } - ); - ProcedureInfo result = procExec.getResult(procId); if (result != null) { if (result.isFailed()) { @@ -156,7 +86,7 @@ public final class ProcedureSyncWait { return result.getResult(); } else { if (procExec.isRunning()) { - throw new IOException("pid= " + procId + "not found"); + throw new IOException("Procedure " + procId + "not found"); } else { throw new IOException("The Master is Aborting"); } @@ -174,7 +104,6 @@ public final class ProcedureSyncWait { public static <T> T waitFor(MasterProcedureEnv env, long waitTime, long waitingTimeForEvents, String purpose, Predicate<T> predicate) throws IOException { final long done = EnvironmentEdgeManager.currentTime() + waitTime; - boolean logged = false; do { T result = predicate.evaluate(); if (result != null && !result.equals(Boolean.FALSE)) { @@ -186,12 +115,7 @@ public final class ProcedureSyncWait { LOG.warn("Interrupted while sleeping, waiting on " + purpose); throw (InterruptedIOException)new InterruptedIOException().initCause(e); } - if (LOG.isTraceEnabled()) { - LOG.trace("waitFor " + purpose); - } else { - if (!logged) LOG.debug("waitFor " + purpose); - } - logged = true; + LOG.debug("Waiting on " + purpose); } while (EnvironmentEdgeManager.currentTime() < done && env.isRunning()); throw new TimeoutIOException("Timed out while waiting on " + purpose); @@ -209,14 +133,44 @@ public final class ProcedureSyncWait { } } + protected static void waitRegionServers(final MasterProcedureEnv env) throws IOException { + final ServerManager sm = env.getMasterServices().getServerManager(); + ProcedureSyncWait.waitFor(env, "server to assign region(s)", + new ProcedureSyncWait.Predicate<Boolean>() { + @Override + public Boolean evaluate() throws IOException { + List<ServerName> servers = sm.createDestinationServersList(); + return servers != null && !servers.isEmpty(); + } + }); + } + + protected static List<HRegionInfo> getRegionsFromMeta(final MasterProcedureEnv env, + final TableName tableName) throws IOException { + return ProcedureSyncWait.waitFor(env, "regions of table=" + tableName + " from meta", + new ProcedureSyncWait.Predicate<List<HRegionInfo>>() { + @Override + public List<HRegionInfo> evaluate() throws IOException { + if (TableName.META_TABLE_NAME.equals(tableName)) { + return new MetaTableLocator().getMetaRegions(env.getMasterServices().getZooKeeper()); + } + return MetaTableAccessor.getTableRegions(env.getMasterServices().getConnection(),tableName); + } + }); + } + protected static void waitRegionInTransition(final MasterProcedureEnv env, final List<HRegionInfo> regions) throws IOException, CoordinatedStateException { - final RegionStates states = env.getAssignmentManager().getRegionStates(); + final AssignmentManager am = env.getMasterServices().getAssignmentManager(); + final RegionStates states = am.getRegionStates(); for (final HRegionInfo region : regions) { ProcedureSyncWait.waitFor(env, "regions " + region.getRegionNameAsString() + " in transition", new ProcedureSyncWait.Predicate<Boolean>() { @Override public Boolean evaluate() throws IOException { + if (states.isRegionInState(region, State.FAILED_OPEN)) { + am.regionOffline(region); + } return !states.isRegionInTransition(region); } }); http://git-wip-us.apache.org/repos/asf/hbase/blob/a3c5a744/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/RSProcedureDispatcher.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/RSProcedureDispatcher.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/RSProcedureDispatcher.java deleted file mode 100644 index 887e272..0000000 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/RSProcedureDispatcher.java +++ /dev/null @@ -1,541 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hadoop.hbase.master.procedure; - -import com.google.common.collect.ArrayListMultimap; - -import java.io.IOException; -import java.net.SocketTimeoutException; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; -import java.util.List; -import java.util.Set; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.hadoop.ipc.RemoteException; -import org.apache.hadoop.hbase.HRegionInfo; -import org.apache.hadoop.hbase.ServerName; -import org.apache.hadoop.hbase.DoNotRetryIOException; -import org.apache.hadoop.hbase.ipc.ServerNotRunningYetException; -import org.apache.hadoop.hbase.master.MasterServices; -import org.apache.hadoop.hbase.master.ServerListener; -import org.apache.hadoop.hbase.procedure2.RemoteProcedureDispatcher; -import org.apache.hadoop.hbase.shaded.com.google.protobuf.ServiceException; -import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; -import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter; -import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.AdminService; -import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegionRequest; -import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegionResponse; -import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.ExecuteProceduresRequest; -import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.ExecuteProceduresResponse; -import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.OpenRegionRequest; -import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.OpenRegionResponse; -import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; - -/** - * A remote procecdure dispatcher for regionservers. - */ -public class RSProcedureDispatcher - extends RemoteProcedureDispatcher<MasterProcedureEnv, ServerName> - implements ServerListener { - private static final Log LOG = LogFactory.getLog(RSProcedureDispatcher.class); - - public static final String RS_RPC_STARTUP_WAIT_TIME_CONF_KEY = - "hbase.regionserver.rpc.startup.waittime"; - private static final int DEFAULT_RS_RPC_STARTUP_WAIT_TIME = 60000; - - private static final int RS_VERSION_WITH_EXEC_PROCS = 0x0201000; // 2.1 - - protected final MasterServices master; - protected final long rsStartupWaitTime; - - public RSProcedureDispatcher(final MasterServices master) { - super(master.getConfiguration()); - - this.master = master; - this.rsStartupWaitTime = master.getConfiguration().getLong( - RS_RPC_STARTUP_WAIT_TIME_CONF_KEY, DEFAULT_RS_RPC_STARTUP_WAIT_TIME); - } - - @Override - public boolean start() { - if (!super.start()) { - return false; - } - - master.getServerManager().registerListener(this); - for (ServerName serverName: master.getServerManager().getOnlineServersList()) { - addNode(serverName); - } - return true; - } - - @Override - public boolean stop() { - if (!super.stop()) { - return false; - } - - master.getServerManager().unregisterListener(this); - return true; - } - - @Override - protected void remoteDispatch(final ServerName serverName, - final Set<RemoteProcedure> operations) { - final int rsVersion = master.getAssignmentManager().getServerVersion(serverName); - if (rsVersion >= RS_VERSION_WITH_EXEC_PROCS) { - LOG.info(String.format( - "Using procedure batch rpc execution for serverName=%s version=%s", - serverName, rsVersion)); - submitTask(new ExecuteProceduresRemoteCall(serverName, operations)); - } else { - LOG.info(String.format( - "Fallback to compat rpc execution for serverName=%s version=%s", - serverName, rsVersion)); - submitTask(new CompatRemoteProcedureResolver(serverName, operations)); - } - } - - protected void abortPendingOperations(final ServerName serverName, - final Set<RemoteProcedure> operations) { - // TODO: Replace with a ServerNotOnlineException() - final IOException e = new DoNotRetryIOException("server not online " + serverName); - final MasterProcedureEnv env = master.getMasterProcedureExecutor().getEnvironment(); - for (RemoteProcedure proc: operations) { - proc.remoteCallFailed(env, serverName, e); - } - } - - public void serverAdded(final ServerName serverName) { - addNode(serverName); - } - - public void serverRemoved(final ServerName serverName) { - removeNode(serverName); - } - - /** - * Base remote call - */ - protected abstract class AbstractRSRemoteCall implements Callable<Void> { - private final ServerName serverName; - - private int numberOfAttemptsSoFar = 0; - private long maxWaitTime = -1; - - public AbstractRSRemoteCall(final ServerName serverName) { - this.serverName = serverName; - } - - public abstract Void call(); - - protected AdminService.BlockingInterface getRsAdmin() throws IOException { - final AdminService.BlockingInterface admin = master.getServerManager().getRsAdmin(serverName); - if (admin == null) { - throw new IOException("Attempting to send OPEN RPC to server " + getServerName() + - " failed because no RPC connection found to this server"); - } - return admin; - } - - protected ServerName getServerName() { - return serverName; - } - - protected boolean scheduleForRetry(final IOException e) { - // Should we wait a little before retrying? If the server is starting it's yes. - final boolean hold = (e instanceof ServerNotRunningYetException); - if (hold) { - LOG.warn(String.format("waiting a little before trying on the same server=%s try=%d", - serverName, numberOfAttemptsSoFar), e); - long now = EnvironmentEdgeManager.currentTime(); - if (now < getMaxWaitTime()) { - if (LOG.isDebugEnabled()) { - LOG.debug(String.format("server is not yet up; waiting up to %dms", - (getMaxWaitTime() - now)), e); - } - submitTask(this, 100, TimeUnit.MILLISECONDS); - return true; - } - - LOG.warn(String.format("server %s is not up for a while; try a new one", serverName), e); - return false; - } - - // In case socket is timed out and the region server is still online, - // the openRegion RPC could have been accepted by the server and - // just the response didn't go through. So we will retry to - // open the region on the same server. - final boolean retry = !hold && (e instanceof SocketTimeoutException - && master.getServerManager().isServerOnline(serverName)); - if (retry) { - // we want to retry as many times as needed as long as the RS is not dead. - if (LOG.isDebugEnabled()) { - LOG.debug(String.format("Retrying to same RegionServer %s because: %s", - serverName, e.getMessage()), e); - } - submitTask(this); - return true; - } - - // trying to send the request elsewhere instead - LOG.warn(String.format("the request should be tried elsewhere instead; server=%s try=%d", - serverName, numberOfAttemptsSoFar), e); - return false; - } - - private long getMaxWaitTime() { - if (this.maxWaitTime < 0) { - // This is the max attempts, not retries, so it should be at least 1. - this.maxWaitTime = EnvironmentEdgeManager.currentTime() + rsStartupWaitTime; - } - return this.maxWaitTime; - } - - protected IOException unwrapException(IOException e) { - if (e instanceof RemoteException) { - e = ((RemoteException)e).unwrapRemoteException(); - } - return e; - } - } - - private interface RemoteProcedureResolver { - void dispatchOpenRequests(MasterProcedureEnv env, List<RegionOpenOperation> operations); - void dispatchCloseRequests(MasterProcedureEnv env, List<RegionCloseOperation> operations); - } - - public void splitAndResolveOperation(final ServerName serverName, - final Set<RemoteProcedure> operations, final RemoteProcedureResolver resolver) { - final MasterProcedureEnv env = master.getMasterProcedureExecutor().getEnvironment(); - final ArrayListMultimap<Class<?>, RemoteOperation> reqsByType = - buildAndGroupRequestByType(env, serverName, operations); - - final List<RegionOpenOperation> openOps = fetchType(reqsByType, RegionOpenOperation.class); - if (!openOps.isEmpty()) resolver.dispatchOpenRequests(env, openOps); - - final List<RegionCloseOperation> closeOps = fetchType(reqsByType, RegionCloseOperation.class); - if (!closeOps.isEmpty()) resolver.dispatchCloseRequests(env, closeOps); - - if (!reqsByType.isEmpty()) { - LOG.warn("unknown request type in the queue: " + reqsByType); - } - } - - // ========================================================================== - // Compatibility calls - // ========================================================================== - protected class ExecuteProceduresRemoteCall extends AbstractRSRemoteCall - implements RemoteProcedureResolver { - private final Set<RemoteProcedure> operations; - - private ExecuteProceduresRequest.Builder request = null; - - public ExecuteProceduresRemoteCall(final ServerName serverName, - final Set<RemoteProcedure> operations) { - super(serverName); - this.operations = operations; - } - - public Void call() { - final MasterProcedureEnv env = master.getMasterProcedureExecutor().getEnvironment(); - - request = ExecuteProceduresRequest.newBuilder(); - splitAndResolveOperation(getServerName(), operations, this); - - try { - final ExecuteProceduresResponse response = sendRequest(getServerName(), request.build()); - remoteCallCompleted(env, response); - } catch (IOException e) { - e = unwrapException(e); - // TODO: In the future some operation may want to bail out early. - // TODO: How many times should we retry (use numberOfAttemptsSoFar) - if (!scheduleForRetry(e)) { - remoteCallFailed(env, e); - } - } - return null; - } - - public void dispatchOpenRequests(final MasterProcedureEnv env, - final List<RegionOpenOperation> operations) { - request.addOpenRegion(buildOpenRegionRequest(env, getServerName(), operations)); - } - - public void dispatchCloseRequests(final MasterProcedureEnv env, - final List<RegionCloseOperation> operations) { - for (RegionCloseOperation op: operations) { - request.addCloseRegion(op.buildCloseRegionRequest(getServerName())); - } - } - - protected ExecuteProceduresResponse sendRequest(final ServerName serverName, - final ExecuteProceduresRequest request) throws IOException { - try { - return getRsAdmin().executeProcedures(null, request); - } catch (ServiceException se) { - throw ProtobufUtil.getRemoteException(se); - } - } - - - private void remoteCallCompleted(final MasterProcedureEnv env, - final ExecuteProceduresResponse response) { - /* - for (RemoteProcedure proc: operations) { - proc.remoteCallCompleted(env, getServerName(), response); - }*/ - } - - private void remoteCallFailed(final MasterProcedureEnv env, final IOException e) { - for (RemoteProcedure proc: operations) { - proc.remoteCallFailed(env, getServerName(), e); - } - } - } - - // ========================================================================== - // Compatibility calls - // Since we don't have a "batch proc-exec" request on the target RS - // we have to chunk the requests by type and dispatch the specific request. - // ========================================================================== - private static OpenRegionRequest buildOpenRegionRequest(final MasterProcedureEnv env, - final ServerName serverName, final List<RegionOpenOperation> operations) { - final OpenRegionRequest.Builder builder = OpenRegionRequest.newBuilder(); - builder.setServerStartCode(serverName.getStartcode()); - builder.setMasterSystemTime(EnvironmentEdgeManager.currentTime()); - for (RegionOpenOperation op: operations) { - builder.addOpenInfo(op.buildRegionOpenInfoRequest(env)); - } - return builder.build(); - } - - private final class OpenRegionRemoteCall extends AbstractRSRemoteCall { - private final List<RegionOpenOperation> operations; - - public OpenRegionRemoteCall(final ServerName serverName, - final List<RegionOpenOperation> operations) { - super(serverName); - this.operations = operations; - } - - @Override - public Void call() { - final MasterProcedureEnv env = master.getMasterProcedureExecutor().getEnvironment(); - final OpenRegionRequest request = buildOpenRegionRequest(env, getServerName(), operations); - - try { - OpenRegionResponse response = sendRequest(getServerName(), request); - remoteCallCompleted(env, response); - } catch (IOException e) { - e = unwrapException(e); - // TODO: In the future some operation may want to bail out early. - // TODO: How many times should we retry (use numberOfAttemptsSoFar) - if (!scheduleForRetry(e)) { - remoteCallFailed(env, e); - } - } - return null; - } - - private OpenRegionResponse sendRequest(final ServerName serverName, - final OpenRegionRequest request) throws IOException { - try { - return getRsAdmin().openRegion(null, request); - } catch (ServiceException se) { - throw ProtobufUtil.getRemoteException(se); - } - } - - private void remoteCallCompleted(final MasterProcedureEnv env, - final OpenRegionResponse response) { - int index = 0; - for (RegionOpenOperation op: operations) { - OpenRegionResponse.RegionOpeningState state = response.getOpeningState(index++); - op.setFailedOpen(state == OpenRegionResponse.RegionOpeningState.FAILED_OPENING); - op.getRemoteProcedure().remoteCallCompleted(env, getServerName(), op); - } - } - - private void remoteCallFailed(final MasterProcedureEnv env, final IOException e) { - for (RegionOpenOperation op: operations) { - op.getRemoteProcedure().remoteCallFailed(env, getServerName(), e); - } - } - } - - private final class CloseRegionRemoteCall extends AbstractRSRemoteCall { - private final RegionCloseOperation operation; - - public CloseRegionRemoteCall(final ServerName serverName, - final RegionCloseOperation operation) { - super(serverName); - this.operation = operation; - } - - @Override - public Void call() { - final MasterProcedureEnv env = master.getMasterProcedureExecutor().getEnvironment(); - final CloseRegionRequest request = operation.buildCloseRegionRequest(getServerName()); - try { - CloseRegionResponse response = sendRequest(getServerName(), request); - remoteCallCompleted(env, response); - } catch (IOException e) { - e = unwrapException(e); - // TODO: In the future some operation may want to bail out early. - // TODO: How many times should we retry (use numberOfAttemptsSoFar) - if (!scheduleForRetry(e)) { - remoteCallFailed(env, e); - } - } - return null; - } - - private CloseRegionResponse sendRequest(final ServerName serverName, - final CloseRegionRequest request) throws IOException { - try { - return getRsAdmin().closeRegion(null, request); - } catch (ServiceException se) { - throw ProtobufUtil.getRemoteException(se); - } - } - - private void remoteCallCompleted(final MasterProcedureEnv env, - final CloseRegionResponse response) { - operation.setClosed(response.getClosed()); - operation.getRemoteProcedure().remoteCallCompleted(env, getServerName(), operation); - } - - private void remoteCallFailed(final MasterProcedureEnv env, final IOException e) { - operation.getRemoteProcedure().remoteCallFailed(env, getServerName(), e); - } - } - - protected class CompatRemoteProcedureResolver implements Callable<Void>, RemoteProcedureResolver { - private final Set<RemoteProcedure> operations; - private final ServerName serverName; - - public CompatRemoteProcedureResolver(final ServerName serverName, - final Set<RemoteProcedure> operations) { - this.serverName = serverName; - this.operations = operations; - } - - @Override - public Void call() { - splitAndResolveOperation(serverName, operations, this); - return null; - } - - public void dispatchOpenRequests(final MasterProcedureEnv env, - final List<RegionOpenOperation> operations) { - submitTask(new OpenRegionRemoteCall(serverName, operations)); - } - - public void dispatchCloseRequests(final MasterProcedureEnv env, - final List<RegionCloseOperation> operations) { - for (RegionCloseOperation op: operations) { - submitTask(new CloseRegionRemoteCall(serverName, op)); - } - } - } - - // ========================================================================== - // RPC Messages - // - ServerOperation: refreshConfig, grant, revoke, ... - // - RegionOperation: open, close, flush, snapshot, ... - // ========================================================================== - public static abstract class ServerOperation extends RemoteOperation { - protected ServerOperation(final RemoteProcedure remoteProcedure) { - super(remoteProcedure); - } - } - - public static abstract class RegionOperation extends RemoteOperation { - private final HRegionInfo regionInfo; - - protected RegionOperation(final RemoteProcedure remoteProcedure, - final HRegionInfo regionInfo) { - super(remoteProcedure); - this.regionInfo = regionInfo; - } - - public HRegionInfo getRegionInfo() { - return this.regionInfo; - } - } - - public static class RegionOpenOperation extends RegionOperation { - private final List<ServerName> favoredNodes; - private final boolean openForReplay; - private boolean failedOpen; - - public RegionOpenOperation(final RemoteProcedure remoteProcedure, - final HRegionInfo regionInfo, final List<ServerName> favoredNodes, - final boolean openForReplay) { - super(remoteProcedure, regionInfo); - this.favoredNodes = favoredNodes; - this.openForReplay = openForReplay; - } - - protected void setFailedOpen(final boolean failedOpen) { - this.failedOpen = failedOpen; - } - - public boolean isFailedOpen() { - return failedOpen; - } - - public OpenRegionRequest.RegionOpenInfo buildRegionOpenInfoRequest( - final MasterProcedureEnv env) { - return RequestConverter.buildRegionOpenInfo(getRegionInfo(), - env.getAssignmentManager().getFavoredNodes(getRegionInfo()), false); - } - } - - public static class RegionCloseOperation extends RegionOperation { - private final ServerName destinationServer; - private boolean closed = false; - - public RegionCloseOperation(final RemoteProcedure remoteProcedure, - final HRegionInfo regionInfo, final ServerName destinationServer) { - super(remoteProcedure, regionInfo); - this.destinationServer = destinationServer; - } - - public ServerName getDestinationServer() { - return destinationServer; - } - - protected void setClosed(final boolean closed) { - this.closed = closed; - } - - public boolean isClosed() { - return closed; - } - - public CloseRegionRequest buildCloseRegionRequest(final ServerName serverName) { - return ProtobufUtil.buildCloseRegionRequest(serverName, - getRegionInfo().getRegionName(), getDestinationServer()); - } - } -} http://git-wip-us.apache.org/repos/asf/hbase/blob/a3c5a744/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/RestoreSnapshotProcedure.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/RestoreSnapshotProcedure.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/RestoreSnapshotProcedure.java index cfd9df9..21709f8 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/RestoreSnapshotProcedure.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/RestoreSnapshotProcedure.java @@ -43,6 +43,7 @@ import org.apache.hadoop.hbase.errorhandling.ForeignException; import org.apache.hadoop.hbase.errorhandling.ForeignExceptionDispatcher; import org.apache.hadoop.hbase.master.MasterFileSystem; import org.apache.hadoop.hbase.master.MetricsSnapshot; +import org.apache.hadoop.hbase.master.RegionStates; import org.apache.hadoop.hbase.monitoring.MonitoredTask; import org.apache.hadoop.hbase.monitoring.TaskMonitor; import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; @@ -415,7 +416,17 @@ public class RestoreSnapshotProcedure try { Connection conn = env.getMasterServices().getConnection(); - // 1. Prepare to restore + // 1. Forces all the RegionStates to be offline + // + // The AssignmentManager keeps all the region states around + // with no possibility to remove them, until the master is restarted. + // This means that a region marked as SPLIT before the restore will never be assigned again. + // To avoid having all states around all the regions are switched to the OFFLINE state, + // which is the same state that the regions will be after a delete table. + forceRegionsOffline(env, regionsToAdd); + forceRegionsOffline(env, regionsToRestore); + forceRegionsOffline(env, regionsToRemove); + getMonitorStatus().setStatus("Preparing to restore each region"); // 2. Applies changes to hbase:meta @@ -485,6 +496,20 @@ public class RestoreSnapshotProcedure } /** + * Make sure that region states of the region list is in OFFLINE state. + * @param env MasterProcedureEnv + * @param hris region info list + **/ + private void forceRegionsOffline(final MasterProcedureEnv env, final List<HRegionInfo> hris) { + RegionStates states = env.getMasterServices().getAssignmentManager().getRegionStates(); + if (hris != null) { + for (HRegionInfo hri: hris) { + states.regionOffline(hri); + } + } + } + + /** * The procedure could be restarted from a different machine. If the variable is null, we need to * retrieve it. * @return traceEnabled http://git-wip-us.apache.org/repos/asf/hbase/blob/a3c5a744/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ServerCrashException.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ServerCrashException.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ServerCrashException.java deleted file mode 100644 index ca351f6..0000000 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ServerCrashException.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hadoop.hbase.master.procedure; - -import org.apache.hadoop.hbase.HBaseIOException; -import org.apache.hadoop.hbase.ServerName; -import org.apache.hadoop.hbase.classification.InterfaceAudience; - -/** - * Passed as Exception by {@link ServerCrashProcedure} - * notifying on-going RIT that server has failed. - */ [email protected] -@SuppressWarnings("serial") -public class ServerCrashException extends HBaseIOException { - private final long procId; - private final ServerName serverName; - - /** - * @param serverName The server that crashed. - */ - public ServerCrashException(long procId, ServerName serverName) { - this.procId = procId; - this.serverName = serverName; - } - - @Override - public String getMessage() { - return "ServerCrashProcedure pid=" + this.procId + ", server=" + this.serverName; - } -} \ No newline at end of file
