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 edu.umd.cs.findbugs.annotations.Nullable; 021import java.io.IOException; 022import org.apache.hadoop.hbase.HBaseIOException; 023import org.apache.hadoop.hbase.ServerName; 024import org.apache.hadoop.hbase.TableName; 025import org.apache.hadoop.hbase.client.RegionInfo; 026import org.apache.hadoop.hbase.client.RegionReplicaUtil; 027import org.apache.hadoop.hbase.client.RetriesExhaustedException; 028import org.apache.hadoop.hbase.master.MetricsAssignmentManager; 029import org.apache.hadoop.hbase.master.RegionState.State; 030import org.apache.hadoop.hbase.master.procedure.AbstractStateMachineRegionProcedure; 031import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; 032import org.apache.hadoop.hbase.master.procedure.ServerCrashProcedure; 033import org.apache.hadoop.hbase.procedure2.Procedure; 034import org.apache.hadoop.hbase.procedure2.ProcedureMetrics; 035import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer; 036import org.apache.hadoop.hbase.procedure2.ProcedureSuspendedException; 037import org.apache.hadoop.hbase.procedure2.ProcedureUtil; 038import org.apache.hadoop.hbase.procedure2.ProcedureYieldException; 039import org.apache.hadoop.hbase.util.RetryCounter; 040import org.apache.yetus.audience.InterfaceAudience; 041import org.slf4j.Logger; 042import org.slf4j.LoggerFactory; 043 044import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; 045 046import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 047import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.RegionStateTransitionState; 048import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.RegionStateTransitionStateData; 049import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.RegionTransitionType; 050import org.apache.hadoop.hbase.shaded.protobuf.generated.ProcedureProtos; 051import org.apache.hadoop.hbase.shaded.protobuf.generated.RegionServerStatusProtos.RegionStateTransition.TransitionCode; 052 053/** 054 * The procedure to deal with the state transition of a region. A region with a TRSP in place is 055 * called RIT, i.e, RegionInTransition. 056 * <p/> 057 * It can be used to assign/unassign/reopen/move a region, and for 058 * {@link #unassign(MasterProcedureEnv, RegionInfo)} and 059 * {@link #reopen(MasterProcedureEnv, RegionInfo)}, you do not need to specify a target server, and 060 * for {@link #assign(MasterProcedureEnv, RegionInfo, ServerName)} and 061 * {@link #move(MasterProcedureEnv, RegionInfo, ServerName)}, if you want to you can provide a 062 * target server. And for {@link #move(MasterProcedureEnv, RegionInfo, ServerName)}, if you do not 063 * specify a targetServer, we will select one randomly. 064 * <p/> 065 * <p/> 066 * The typical state transition for assigning a region is: 067 * 068 * <pre> 069 * GET_ASSIGN_CANDIDATE ------> OPEN -----> CONFIRM_OPENED 070 * </pre> 071 * 072 * Notice that, if there are failures we may go back to the {@code GET_ASSIGN_CANDIDATE} state to 073 * try again. 074 * <p/> 075 * The typical state transition for unassigning a region is: 076 * 077 * <pre> 078 * CLOSE -----> CONFIRM_CLOSED 079 * </pre> 080 * 081 * Here things go a bit different, if there are failures, especially that if there is a server 082 * crash, we will go to the {@code GET_ASSIGN_CANDIDATE} state to bring the region online first, and 083 * then go through the normal way to unassign it. 084 * <p/> 085 * The typical state transition for reopening/moving a region is: 086 * 087 * <pre> 088 * CLOSE -----> CONFIRM_CLOSED -----> GET_ASSIGN_CANDIDATE ------> OPEN -----> CONFIRM_OPENED 089 * </pre> 090 * 091 * The retry logic is the same with the above assign/unassign. 092 * <p/> 093 * Notice that, although we allow specify a target server, it just acts as a candidate, we do not 094 * guarantee that the region will finally be on the target server. If this is important for you, you 095 * should check whether the region is on the target server after the procedure is finished. 096 * <p/> 097 * When you want to schedule a TRSP, please check whether there is still one for this region, and 098 * the check should be under the RegionStateNode lock. We will remove the TRSP from a 099 * RegionStateNode when we are done, see the code in {@code reportTransition} method below. There 100 * could be at most one TRSP for a give region. 101 */ 102@InterfaceAudience.Private 103public class TransitRegionStateProcedure 104 extends AbstractStateMachineRegionProcedure<RegionStateTransitionState> { 105 106 private static final Logger LOG = LoggerFactory.getLogger(TransitRegionStateProcedure.class); 107 108 private TransitionType type; 109 110 private RegionStateTransitionState initialState; 111 112 private RegionStateTransitionState lastState; 113 114 // the candidate where we want to assign the region to. 115 private ServerName assignCandidate; 116 117 private boolean forceNewPlan; 118 119 private RetryCounter retryCounter; 120 121 private RegionRemoteProcedureBase remoteProc; 122 123 public TransitRegionStateProcedure() { 124 } 125 126 private void setInitalAndLastState() { 127 switch (type) { 128 case ASSIGN: 129 initialState = RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE; 130 lastState = RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_OPENED; 131 break; 132 case UNASSIGN: 133 initialState = RegionStateTransitionState.REGION_STATE_TRANSITION_CLOSE; 134 lastState = RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_CLOSED; 135 break; 136 case MOVE: 137 case REOPEN: 138 initialState = RegionStateTransitionState.REGION_STATE_TRANSITION_CLOSE; 139 lastState = RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_OPENED; 140 break; 141 default: 142 throw new IllegalArgumentException("Unknown TransitionType: " + type); 143 } 144 } 145 146 @VisibleForTesting 147 protected TransitRegionStateProcedure(MasterProcedureEnv env, RegionInfo hri, 148 ServerName assignCandidate, boolean forceNewPlan, TransitionType type) { 149 super(env, hri); 150 this.assignCandidate = assignCandidate; 151 this.forceNewPlan = forceNewPlan; 152 this.type = type; 153 setInitalAndLastState(); 154 155 // when do reopen TRSP, let the rs know the targetServer so it can keep some info on close 156 if (type == TransitionType.REOPEN) { 157 this.assignCandidate = getRegionStateNode(env).getRegionLocation(); 158 } 159 } 160 161 @Override 162 public TableOperationType getTableOperationType() { 163 // TODO: maybe we should make another type here, REGION_TRANSITION? 164 return TableOperationType.REGION_EDIT; 165 } 166 167 @Override 168 protected boolean waitInitialized(MasterProcedureEnv env) { 169 if (TableName.isMetaTableName(getTableName())) { 170 return false; 171 } 172 // First we need meta to be loaded, and second, if meta is not online then we will likely to 173 // fail when updating meta so we wait until it is assigned. 174 AssignmentManager am = env.getAssignmentManager(); 175 return am.waitMetaLoaded(this) || am.waitMetaAssigned(this, getRegion()); 176 } 177 178 private void queueAssign(MasterProcedureEnv env, RegionStateNode regionNode) 179 throws ProcedureSuspendedException { 180 boolean retain = false; 181 if (forceNewPlan) { 182 // set the region location to null if forceNewPlan is true 183 regionNode.setRegionLocation(null); 184 } else { 185 if (assignCandidate != null) { 186 retain = assignCandidate.equals(regionNode.getLastHost()); 187 regionNode.setRegionLocation(assignCandidate); 188 } else if (regionNode.getLastHost() != null) { 189 retain = true; 190 LOG.info("Setting lastHost as the region location {}", regionNode.getLastHost()); 191 regionNode.setRegionLocation(regionNode.getLastHost()); 192 } 193 } 194 LOG.info("Starting {}; {}; forceNewPlan={}, retain={}", this, regionNode.toShortString(), 195 forceNewPlan, retain); 196 env.getAssignmentManager().queueAssign(regionNode); 197 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_OPEN); 198 if (regionNode.getProcedureEvent().suspendIfNotReady(this)) { 199 throw new ProcedureSuspendedException(); 200 } 201 } 202 203 private void openRegion(MasterProcedureEnv env, RegionStateNode regionNode) throws IOException { 204 ServerName loc = regionNode.getRegionLocation(); 205 if (loc == null) { 206 LOG.warn("No location specified for {}, jump back to state {} to get one", getRegion(), 207 RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE); 208 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE); 209 return; 210 } 211 env.getAssignmentManager().regionOpening(regionNode); 212 addChildProcedure(new OpenRegionProcedure(this, getRegion(), loc)); 213 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_OPENED); 214 } 215 216 private Flow confirmOpened(MasterProcedureEnv env, RegionStateNode regionNode) 217 throws IOException { 218 if (regionNode.isInState(State.OPEN)) { 219 retryCounter = null; 220 if (lastState == RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_OPENED) { 221 // we are the last state, finish 222 regionNode.unsetProcedure(this); 223 ServerCrashProcedure.updateProgress(env, getParentProcId()); 224 return Flow.NO_MORE_STATE; 225 } 226 // It is possible that we arrive here but confirm opened is not the last state, for example, 227 // when merging or splitting a region, we unassign the region from a RS and the RS is crashed, 228 // then there will be recovered edits for this region, we'd better make the region online 229 // again and then unassign it, otherwise we have to fail the merge/split procedure as we may 230 // loss data. 231 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_CLOSE); 232 return Flow.HAS_MORE_STATE; 233 } 234 235 int retries = env.getAssignmentManager().getRegionStates().addToFailedOpen(regionNode) 236 .incrementAndGetRetries(); 237 int maxAttempts = env.getAssignmentManager().getAssignMaxAttempts(); 238 LOG.info("Retry={} of max={}; {}; {}", retries, maxAttempts, this, regionNode.toShortString()); 239 240 if (retries >= maxAttempts) { 241 env.getAssignmentManager().regionFailedOpen(regionNode, true); 242 setFailure(getClass().getSimpleName(), new RetriesExhaustedException( 243 "Max attempts " + env.getAssignmentManager().getAssignMaxAttempts() + " exceeded")); 244 regionNode.unsetProcedure(this); 245 return Flow.NO_MORE_STATE; 246 } 247 248 env.getAssignmentManager().regionFailedOpen(regionNode, false); 249 // we failed to assign the region, force a new plan 250 forceNewPlan = true; 251 regionNode.setRegionLocation(null); 252 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE); 253 254 if (retries > env.getAssignmentManager().getAssignRetryImmediatelyMaxAttempts()) { 255 // Throw exception to backoff and retry when failed open too many times 256 throw new HBaseIOException("Failed confirm OPEN of " + regionNode + 257 " (remote log may yield more detail on why)."); 258 } else { 259 // Here we do not throw exception because we want to the region to be online ASAP 260 return Flow.HAS_MORE_STATE; 261 } 262 } 263 264 private void closeRegion(MasterProcedureEnv env, RegionStateNode regionNode) throws IOException { 265 if (regionNode.isInState(State.OPEN, State.CLOSING, State.MERGING, State.SPLITTING)) { 266 // this is the normal case 267 env.getAssignmentManager().regionClosing(regionNode); 268 addChildProcedure(new CloseRegionProcedure(this, getRegion(), regionNode.getRegionLocation(), 269 assignCandidate)); 270 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_CLOSED); 271 } else { 272 forceNewPlan = true; 273 regionNode.setRegionLocation(null); 274 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE); 275 } 276 } 277 278 private Flow confirmClosed(MasterProcedureEnv env, RegionStateNode regionNode) 279 throws IOException { 280 if (regionNode.isInState(State.CLOSED)) { 281 retryCounter = null; 282 if (lastState == RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_CLOSED) { 283 // we are the last state, finish 284 regionNode.unsetProcedure(this); 285 return Flow.NO_MORE_STATE; 286 } 287 // This means we need to open the region again, should be a move or reopen 288 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE); 289 return Flow.HAS_MORE_STATE; 290 } 291 if (regionNode.isInState(State.CLOSING)) { 292 // This is possible, think the target RS crashes and restarts immediately, the close region 293 // operation will return a NotServingRegionException soon, we can only recover after SCP takes 294 // care of this RS. So here we throw an IOException to let upper layer to retry with backoff. 295 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_CLOSE); 296 throw new HBaseIOException("Failed to close region"); 297 } 298 // abnormally closed, need to reopen it, no matter what is the last state, see the comment in 299 // confirmOpened for more details that why we need to reopen the region first even if we just 300 // want to close it. 301 // The only exception is for non-default replica, where we do not need to deal with recovered 302 // edits. Notice that the region will remain in ABNORMALLY_CLOSED state, the upper layer need to 303 // deal with this state. For non-default replica, this is usually the same with CLOSED. 304 assert regionNode.isInState(State.ABNORMALLY_CLOSED); 305 if (!RegionReplicaUtil.isDefaultReplica(getRegion()) && 306 lastState == RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_CLOSED) { 307 regionNode.unsetProcedure(this); 308 return Flow.NO_MORE_STATE; 309 } 310 retryCounter = null; 311 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE); 312 return Flow.HAS_MORE_STATE; 313 } 314 315 // Override to lock RegionStateNode 316 @SuppressWarnings("rawtypes") 317 @Override 318 protected Procedure[] execute(MasterProcedureEnv env) 319 throws ProcedureSuspendedException, ProcedureYieldException, InterruptedException { 320 RegionStateNode regionNode = 321 env.getAssignmentManager().getRegionStates().getOrCreateRegionStateNode(getRegion()); 322 regionNode.lock(); 323 try { 324 return super.execute(env); 325 } finally { 326 regionNode.unlock(); 327 } 328 } 329 330 private RegionStateNode getRegionStateNode(MasterProcedureEnv env) { 331 return env.getAssignmentManager().getRegionStates().getOrCreateRegionStateNode(getRegion()); 332 } 333 334 @Override 335 protected Flow executeFromState(MasterProcedureEnv env, RegionStateTransitionState state) 336 throws ProcedureSuspendedException, ProcedureYieldException, InterruptedException { 337 RegionStateNode regionNode = getRegionStateNode(env); 338 try { 339 switch (state) { 340 case REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE: 341 // Need to do some sanity check for replica region, if the region does not exist at 342 // master, do not try to assign the replica region, log error and return. 343 if (!RegionReplicaUtil.isDefaultReplica(regionNode.getRegionInfo())) { 344 RegionInfo defaultRI = 345 RegionReplicaUtil.getRegionInfoForDefaultReplica(regionNode.getRegionInfo()); 346 if (env.getMasterServices().getAssignmentManager().getRegionStates(). 347 getRegionStateNode(defaultRI) == null) { 348 LOG.error( 349 "Cannot assign replica region {} because its primary region {} does not exist.", 350 regionNode.getRegionInfo(), defaultRI); 351 return Flow.NO_MORE_STATE; 352 } 353 } 354 queueAssign(env, regionNode); 355 return Flow.HAS_MORE_STATE; 356 case REGION_STATE_TRANSITION_OPEN: 357 openRegion(env, regionNode); 358 return Flow.HAS_MORE_STATE; 359 case REGION_STATE_TRANSITION_CONFIRM_OPENED: 360 return confirmOpened(env, regionNode); 361 case REGION_STATE_TRANSITION_CLOSE: 362 closeRegion(env, regionNode); 363 return Flow.HAS_MORE_STATE; 364 case REGION_STATE_TRANSITION_CONFIRM_CLOSED: 365 return confirmClosed(env, regionNode); 366 default: 367 throw new UnsupportedOperationException("unhandled state=" + state); 368 } 369 } catch (IOException e) { 370 if (retryCounter == null) { 371 retryCounter = ProcedureUtil.createRetryCounter(env.getMasterConfiguration()); 372 } 373 long backoff = retryCounter.getBackoffTimeAndIncrementAttempts(); 374 LOG.warn( 375 "Failed transition, suspend {}secs {}; {}; waiting on rectified condition fixed " + 376 "by other Procedure or operator intervention", 377 backoff / 1000, this, regionNode.toShortString(), e); 378 setTimeout(Math.toIntExact(backoff)); 379 setState(ProcedureProtos.ProcedureState.WAITING_TIMEOUT); 380 skipPersistence(); 381 throw new ProcedureSuspendedException(); 382 } 383 } 384 385 /** 386 * At end of timeout, wake ourselves up so we run again. 387 */ 388 @Override 389 protected synchronized boolean setTimeoutFailure(MasterProcedureEnv env) { 390 setState(ProcedureProtos.ProcedureState.RUNNABLE); 391 env.getProcedureScheduler().addFront(this); 392 return false; // 'false' means that this procedure handled the timeout 393 } 394 395 // Should be called with RegionStateNode locked 396 public void reportTransition(MasterProcedureEnv env, RegionStateNode regionNode, 397 ServerName serverName, TransitionCode code, long seqId, long procId) throws IOException { 398 if (remoteProc == null) { 399 LOG.warn( 400 "There is no outstanding remote region procedure for {}, serverName={}, code={}," + 401 " seqId={}, proc={}, should be a retry, ignore", 402 regionNode, serverName, code, seqId, this); 403 return; 404 } 405 // The procId could be -1 if it is from an old region server, we need to deal with it so that we 406 // can do rolling upgraing. 407 if (procId >= 0 && remoteProc.getProcId() != procId) { 408 LOG.warn( 409 "The pid of remote region procedure for {} is {}, the reported pid={}, serverName={}," + 410 " code={}, seqId={}, proc={}, should be a retry, ignore", 411 regionNode, remoteProc.getProcId(), procId, serverName, code, seqId, this); 412 return; 413 } 414 remoteProc.reportTransition(env, regionNode, serverName, code, seqId); 415 } 416 417 // Should be called with RegionStateNode locked 418 public void serverCrashed(MasterProcedureEnv env, RegionStateNode regionNode, 419 ServerName serverName) throws IOException { 420 // force to assign to a new candidate server 421 // AssignmentManager#regionClosedAbnormally will set region location to null 422 // TODO: the forceNewPlan flag not be persistent so if master crash then the flag will be lost. 423 // But assign to old server is not big deal because it not effect correctness. 424 // See HBASE-23035 for more details. 425 forceNewPlan = true; 426 if (remoteProc != null) { 427 // this means we are waiting for the sub procedure, so wake it up 428 remoteProc.serverCrashed(env, regionNode, serverName); 429 } else { 430 // we are in RUNNING state, just update the region state, and we will process it later. 431 env.getAssignmentManager().regionClosedAbnormally(regionNode); 432 } 433 } 434 435 void attachRemoteProc(RegionRemoteProcedureBase proc) { 436 this.remoteProc = proc; 437 } 438 439 void unattachRemoteProc(RegionRemoteProcedureBase proc) { 440 assert this.remoteProc == proc; 441 this.remoteProc = null; 442 } 443 444 // will be called after we finish loading the meta entry for this region. 445 // used to change the state of the region node if we have a sub procedure, as we may not persist 446 // the state to meta yet. See the code in RegionRemoteProcedureBase.execute for more details. 447 void stateLoaded(AssignmentManager am, RegionStateNode regionNode) { 448 if (remoteProc != null) { 449 remoteProc.stateLoaded(am, regionNode); 450 } 451 } 452 453 @Override 454 protected void rollbackState(MasterProcedureEnv env, RegionStateTransitionState state) 455 throws IOException, InterruptedException { 456 // no rollback 457 throw new UnsupportedOperationException(); 458 } 459 460 @Override 461 protected RegionStateTransitionState getState(int stateId) { 462 return RegionStateTransitionState.forNumber(stateId); 463 } 464 465 @Override 466 protected int getStateId(RegionStateTransitionState state) { 467 return state.getNumber(); 468 } 469 470 @Override 471 protected RegionStateTransitionState getInitialState() { 472 return initialState; 473 } 474 475 private static TransitionType convert(RegionTransitionType type) { 476 switch (type) { 477 case ASSIGN: 478 return TransitionType.ASSIGN; 479 case UNASSIGN: 480 return TransitionType.UNASSIGN; 481 case MOVE: 482 return TransitionType.MOVE; 483 case REOPEN: 484 return TransitionType.REOPEN; 485 default: 486 throw new IllegalArgumentException("Unknown RegionTransitionType: " + type); 487 } 488 } 489 490 private static RegionTransitionType convert(TransitionType type) { 491 switch (type) { 492 case ASSIGN: 493 return RegionTransitionType.ASSIGN; 494 case UNASSIGN: 495 return RegionTransitionType.UNASSIGN; 496 case MOVE: 497 return RegionTransitionType.MOVE; 498 case REOPEN: 499 return RegionTransitionType.REOPEN; 500 default: 501 throw new IllegalArgumentException("Unknown TransitionType: " + type); 502 } 503 } 504 505 @Override 506 protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException { 507 super.serializeStateData(serializer); 508 RegionStateTransitionStateData.Builder builder = RegionStateTransitionStateData.newBuilder() 509 .setType(convert(type)).setForceNewPlan(forceNewPlan); 510 if (assignCandidate != null) { 511 builder.setAssignCandidate(ProtobufUtil.toServerName(assignCandidate)); 512 } 513 serializer.serialize(builder.build()); 514 } 515 516 @Override 517 protected void deserializeStateData(ProcedureStateSerializer serializer) throws IOException { 518 super.deserializeStateData(serializer); 519 RegionStateTransitionStateData data = 520 serializer.deserialize(RegionStateTransitionStateData.class); 521 type = convert(data.getType()); 522 setInitalAndLastState(); 523 forceNewPlan = data.getForceNewPlan(); 524 if (data.hasAssignCandidate()) { 525 assignCandidate = ProtobufUtil.toServerName(data.getAssignCandidate()); 526 } 527 } 528 529 @Override 530 protected ProcedureMetrics getProcedureMetrics(MasterProcedureEnv env) { 531 MetricsAssignmentManager metrics = env.getAssignmentManager().getAssignmentManagerMetrics(); 532 switch (type) { 533 case ASSIGN: 534 return metrics.getAssignProcMetrics(); 535 case UNASSIGN: 536 return metrics.getUnassignProcMetrics(); 537 case MOVE: 538 return metrics.getMoveProcMetrics(); 539 case REOPEN: 540 return metrics.getReopenProcMetrics(); 541 default: 542 throw new IllegalArgumentException("Unknown transition type: " + type); 543 } 544 } 545 546 @Override 547 public void toStringClassDetails(StringBuilder sb) { 548 super.toStringClassDetails(sb); 549 if (initialState == RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE) { 550 sb.append(", ASSIGN"); 551 } else if (lastState == RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_CLOSED) { 552 sb.append(", UNASSIGN"); 553 } else { 554 sb.append(", REOPEN/MOVE"); 555 } 556 } 557 558 private static TransitRegionStateProcedure setOwner(MasterProcedureEnv env, 559 TransitRegionStateProcedure proc) { 560 proc.setOwner(env.getRequestUser().getShortName()); 561 return proc; 562 } 563 564 public enum TransitionType { 565 ASSIGN, UNASSIGN, MOVE, REOPEN 566 } 567 568 // Be careful that, when you call these 4 methods below, you need to manually attach the returned 569 // procedure with the RegionStateNode, otherwise the procedure will quit immediately without doing 570 // anything. See the comment in executeFromState to find out why we need this assumption. 571 public static TransitRegionStateProcedure assign(MasterProcedureEnv env, RegionInfo region, 572 @Nullable ServerName targetServer) { 573 return assign(env, region, false, targetServer); 574 } 575 576 public static TransitRegionStateProcedure assign(MasterProcedureEnv env, RegionInfo region, 577 boolean forceNewPlan, @Nullable ServerName targetServer) { 578 return setOwner(env, new TransitRegionStateProcedure(env, region, targetServer, forceNewPlan, 579 TransitionType.ASSIGN)); 580 } 581 582 public static TransitRegionStateProcedure unassign(MasterProcedureEnv env, RegionInfo region) { 583 return setOwner(env, 584 new TransitRegionStateProcedure(env, region, null, false, TransitionType.UNASSIGN)); 585 } 586 587 public static TransitRegionStateProcedure reopen(MasterProcedureEnv env, RegionInfo region) { 588 return setOwner(env, 589 new TransitRegionStateProcedure(env, region, null, false, TransitionType.REOPEN)); 590 } 591 592 public static TransitRegionStateProcedure move(MasterProcedureEnv env, RegionInfo region, 593 @Nullable ServerName targetServer) { 594 return setOwner(env, new TransitRegionStateProcedure(env, region, targetServer, 595 targetServer == null, TransitionType.MOVE)); 596 } 597}