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 156 @Override 157 public TableOperationType getTableOperationType() { 158 // TODO: maybe we should make another type here, REGION_TRANSITION? 159 return TableOperationType.REGION_EDIT; 160 } 161 162 @Override 163 protected boolean waitInitialized(MasterProcedureEnv env) { 164 if (TableName.isMetaTableName(getTableName())) { 165 return false; 166 } 167 // First we need meta to be loaded, and second, if meta is not online then we will likely to 168 // fail when updating meta so we wait until it is assigned. 169 AssignmentManager am = env.getAssignmentManager(); 170 return am.waitMetaLoaded(this) || am.waitMetaAssigned(this, getRegion()); 171 } 172 173 private void queueAssign(MasterProcedureEnv env, RegionStateNode regionNode) 174 throws ProcedureSuspendedException { 175 boolean retain = false; 176 if (forceNewPlan) { 177 // set the region location to null if forceNewPlan is true 178 regionNode.setRegionLocation(null); 179 } else { 180 if (assignCandidate != null) { 181 retain = assignCandidate.equals(regionNode.getLastHost()); 182 regionNode.setRegionLocation(assignCandidate); 183 } else if (regionNode.getLastHost() != null) { 184 retain = true; 185 LOG.info("Setting lastHost as the region location {}", regionNode.getLastHost()); 186 regionNode.setRegionLocation(regionNode.getLastHost()); 187 } 188 } 189 LOG.info("Starting {}; {}; forceNewPlan={}, retain={}", this, regionNode.toShortString(), 190 forceNewPlan, retain); 191 env.getAssignmentManager().queueAssign(regionNode); 192 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_OPEN); 193 if (regionNode.getProcedureEvent().suspendIfNotReady(this)) { 194 throw new ProcedureSuspendedException(); 195 } 196 } 197 198 private void openRegion(MasterProcedureEnv env, RegionStateNode regionNode) throws IOException { 199 ServerName loc = regionNode.getRegionLocation(); 200 if (loc == null) { 201 LOG.warn("No location specified for {}, jump back to state {} to get one", getRegion(), 202 RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE); 203 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE); 204 return; 205 } 206 env.getAssignmentManager().regionOpening(regionNode); 207 addChildProcedure(new OpenRegionProcedure(this, getRegion(), loc)); 208 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_OPENED); 209 } 210 211 private Flow confirmOpened(MasterProcedureEnv env, RegionStateNode regionNode) 212 throws IOException { 213 if (regionNode.isInState(State.OPEN)) { 214 retryCounter = null; 215 if (lastState == RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_OPENED) { 216 // we are the last state, finish 217 regionNode.unsetProcedure(this); 218 ServerCrashProcedure.updateProgress(env, getParentProcId()); 219 return Flow.NO_MORE_STATE; 220 } 221 // It is possible that we arrive here but confirm opened is not the last state, for example, 222 // when merging or splitting a region, we unassign the region from a RS and the RS is crashed, 223 // then there will be recovered edits for this region, we'd better make the region online 224 // again and then unassign it, otherwise we have to fail the merge/split procedure as we may 225 // loss data. 226 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_CLOSE); 227 return Flow.HAS_MORE_STATE; 228 } 229 230 int retries = env.getAssignmentManager().getRegionStates().addToFailedOpen(regionNode) 231 .incrementAndGetRetries(); 232 int maxAttempts = env.getAssignmentManager().getAssignMaxAttempts(); 233 LOG.info("Retry={} of max={}; {}; {}", retries, maxAttempts, this, regionNode.toShortString()); 234 235 if (retries >= maxAttempts) { 236 env.getAssignmentManager().regionFailedOpen(regionNode, true); 237 setFailure(getClass().getSimpleName(), new RetriesExhaustedException( 238 "Max attempts " + env.getAssignmentManager().getAssignMaxAttempts() + " exceeded")); 239 regionNode.unsetProcedure(this); 240 return Flow.NO_MORE_STATE; 241 } 242 243 env.getAssignmentManager().regionFailedOpen(regionNode, false); 244 // we failed to assign the region, force a new plan 245 forceNewPlan = true; 246 regionNode.setRegionLocation(null); 247 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE); 248 249 if (retries > env.getAssignmentManager().getAssignRetryImmediatelyMaxAttempts()) { 250 // Throw exception to backoff and retry when failed open too many times 251 throw new HBaseIOException("Failed to open region"); 252 } else { 253 // Here we do not throw exception because we want to the region to be online ASAP 254 return Flow.HAS_MORE_STATE; 255 } 256 } 257 258 private void closeRegion(MasterProcedureEnv env, RegionStateNode regionNode) throws IOException { 259 if (regionNode.isInState(State.OPEN, State.CLOSING, State.MERGING, State.SPLITTING)) { 260 // this is the normal case 261 env.getAssignmentManager().regionClosing(regionNode); 262 addChildProcedure(new CloseRegionProcedure(this, getRegion(), regionNode.getRegionLocation(), 263 assignCandidate)); 264 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_CLOSED); 265 } else { 266 forceNewPlan = true; 267 regionNode.setRegionLocation(null); 268 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE); 269 } 270 } 271 272 private Flow confirmClosed(MasterProcedureEnv env, RegionStateNode regionNode) 273 throws IOException { 274 if (regionNode.isInState(State.CLOSED)) { 275 retryCounter = null; 276 if (lastState == RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_CLOSED) { 277 // we are the last state, finish 278 regionNode.unsetProcedure(this); 279 return Flow.NO_MORE_STATE; 280 } 281 // This means we need to open the region again, should be a move or reopen 282 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE); 283 return Flow.HAS_MORE_STATE; 284 } 285 if (regionNode.isInState(State.CLOSING)) { 286 // This is possible, think the target RS crashes and restarts immediately, the close region 287 // operation will return a NotServingRegionException soon, we can only recover after SCP takes 288 // care of this RS. So here we throw an IOException to let upper layer to retry with backoff. 289 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_CLOSE); 290 throw new HBaseIOException("Failed to close region"); 291 } 292 // abnormally closed, need to reopen it, no matter what is the last state, see the comment in 293 // confirmOpened for more details that why we need to reopen the region first even if we just 294 // want to close it. 295 // The only exception is for non-default replica, where we do not need to deal with recovered 296 // edits. Notice that the region will remain in ABNORMALLY_CLOSED state, the upper layer need to 297 // deal with this state. For non-default replica, this is usually the same with CLOSED. 298 assert regionNode.isInState(State.ABNORMALLY_CLOSED); 299 if (!RegionReplicaUtil.isDefaultReplica(getRegion()) && 300 lastState == RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_CLOSED) { 301 regionNode.unsetProcedure(this); 302 return Flow.NO_MORE_STATE; 303 } 304 retryCounter = null; 305 setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE); 306 return Flow.HAS_MORE_STATE; 307 } 308 309 // Override to lock RegionStateNode 310 @SuppressWarnings("rawtypes") 311 @Override 312 protected Procedure[] execute(MasterProcedureEnv env) 313 throws ProcedureSuspendedException, ProcedureYieldException, InterruptedException { 314 RegionStateNode regionNode = 315 env.getAssignmentManager().getRegionStates().getOrCreateRegionStateNode(getRegion()); 316 regionNode.lock(); 317 try { 318 return super.execute(env); 319 } finally { 320 regionNode.unlock(); 321 } 322 } 323 324 private RegionStateNode getRegionStateNode(MasterProcedureEnv env) { 325 return env.getAssignmentManager().getRegionStates().getOrCreateRegionStateNode(getRegion()); 326 } 327 328 @Override 329 protected Flow executeFromState(MasterProcedureEnv env, RegionStateTransitionState state) 330 throws ProcedureSuspendedException, ProcedureYieldException, InterruptedException { 331 RegionStateNode regionNode = getRegionStateNode(env); 332 try { 333 switch (state) { 334 case REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE: 335 queueAssign(env, regionNode); 336 return Flow.HAS_MORE_STATE; 337 case REGION_STATE_TRANSITION_OPEN: 338 openRegion(env, regionNode); 339 return Flow.HAS_MORE_STATE; 340 case REGION_STATE_TRANSITION_CONFIRM_OPENED: 341 return confirmOpened(env, regionNode); 342 case REGION_STATE_TRANSITION_CLOSE: 343 closeRegion(env, regionNode); 344 return Flow.HAS_MORE_STATE; 345 case REGION_STATE_TRANSITION_CONFIRM_CLOSED: 346 return confirmClosed(env, regionNode); 347 default: 348 throw new UnsupportedOperationException("unhandled state=" + state); 349 } 350 } catch (IOException e) { 351 if (retryCounter == null) { 352 retryCounter = ProcedureUtil.createRetryCounter(env.getMasterConfiguration()); 353 } 354 long backoff = retryCounter.getBackoffTimeAndIncrementAttempts(); 355 LOG.warn( 356 "Failed transition, suspend {}secs {}; {}; waiting on rectified condition fixed " + 357 "by other Procedure or operator intervention", 358 backoff / 1000, this, regionNode.toShortString(), e); 359 setTimeout(Math.toIntExact(backoff)); 360 setState(ProcedureProtos.ProcedureState.WAITING_TIMEOUT); 361 skipPersistence(); 362 throw new ProcedureSuspendedException(); 363 } 364 } 365 366 /** 367 * At end of timeout, wake ourselves up so we run again. 368 */ 369 @Override 370 protected synchronized boolean setTimeoutFailure(MasterProcedureEnv env) { 371 setState(ProcedureProtos.ProcedureState.RUNNABLE); 372 env.getProcedureScheduler().addFront(this); 373 return false; // 'false' means that this procedure handled the timeout 374 } 375 376 // Should be called with RegionStateNode locked 377 public void reportTransition(MasterProcedureEnv env, RegionStateNode regionNode, 378 ServerName serverName, TransitionCode code, long seqId, long procId) throws IOException { 379 if (remoteProc == null) { 380 LOG.warn( 381 "There is no outstanding remote region procedure for {}, serverName={}, code={}," + 382 " seqId={}, proc={}, should be a retry, ignore", 383 regionNode, serverName, code, seqId, this); 384 return; 385 } 386 // The procId could be -1 if it is from an old region server, we need to deal with it so that we 387 // can do rolling upgraing. 388 if (procId >= 0 && remoteProc.getProcId() != procId) { 389 LOG.warn( 390 "The pid of remote region procedure for {} is {}, the reported pid={}, serverName={}," + 391 " code={}, seqId={}, proc={}, should be a retry, ignore", 392 regionNode, remoteProc.getProcId(), procId, serverName, code, seqId, this); 393 return; 394 } 395 remoteProc.reportTransition(env, regionNode, serverName, code, seqId); 396 } 397 398 // Should be called with RegionStateNode locked 399 public void serverCrashed(MasterProcedureEnv env, RegionStateNode regionNode, 400 ServerName serverName) throws IOException { 401 // force to assign to a new candidate server 402 // AssignmentManager#regionClosedAbnormally will set region location to null 403 // TODO: the forceNewPlan flag not be persistent so if master crash then the flag will be lost. 404 // But assign to old server is not big deal because it not effect correctness. 405 // See HBASE-23035 for more details. 406 forceNewPlan = true; 407 if (remoteProc != null) { 408 // this means we are waiting for the sub procedure, so wake it up 409 remoteProc.serverCrashed(env, regionNode, serverName); 410 } else { 411 // we are in RUNNING state, just update the region state, and we will process it later. 412 env.getAssignmentManager().regionClosedAbnormally(regionNode); 413 } 414 } 415 416 void attachRemoteProc(RegionRemoteProcedureBase proc) { 417 this.remoteProc = proc; 418 } 419 420 void unattachRemoteProc(RegionRemoteProcedureBase proc) { 421 assert this.remoteProc == proc; 422 this.remoteProc = null; 423 } 424 425 // will be called after we finish loading the meta entry for this region. 426 // used to change the state of the region node if we have a sub procedure, as we may not persist 427 // the state to meta yet. See the code in RegionRemoteProcedureBase.execute for more details. 428 void stateLoaded(AssignmentManager am, RegionStateNode regionNode) { 429 if (remoteProc != null) { 430 remoteProc.stateLoaded(am, regionNode); 431 } 432 } 433 434 @Override 435 protected void rollbackState(MasterProcedureEnv env, RegionStateTransitionState state) 436 throws IOException, InterruptedException { 437 // no rollback 438 throw new UnsupportedOperationException(); 439 } 440 441 @Override 442 protected RegionStateTransitionState getState(int stateId) { 443 return RegionStateTransitionState.forNumber(stateId); 444 } 445 446 @Override 447 protected int getStateId(RegionStateTransitionState state) { 448 return state.getNumber(); 449 } 450 451 @Override 452 protected RegionStateTransitionState getInitialState() { 453 return initialState; 454 } 455 456 private static TransitionType convert(RegionTransitionType type) { 457 switch (type) { 458 case ASSIGN: 459 return TransitionType.ASSIGN; 460 case UNASSIGN: 461 return TransitionType.UNASSIGN; 462 case MOVE: 463 return TransitionType.MOVE; 464 case REOPEN: 465 return TransitionType.REOPEN; 466 default: 467 throw new IllegalArgumentException("Unknown RegionTransitionType: " + type); 468 } 469 } 470 471 private static RegionTransitionType convert(TransitionType type) { 472 switch (type) { 473 case ASSIGN: 474 return RegionTransitionType.ASSIGN; 475 case UNASSIGN: 476 return RegionTransitionType.UNASSIGN; 477 case MOVE: 478 return RegionTransitionType.MOVE; 479 case REOPEN: 480 return RegionTransitionType.REOPEN; 481 default: 482 throw new IllegalArgumentException("Unknown TransitionType: " + type); 483 } 484 } 485 486 @Override 487 protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException { 488 super.serializeStateData(serializer); 489 RegionStateTransitionStateData.Builder builder = RegionStateTransitionStateData.newBuilder() 490 .setType(convert(type)).setForceNewPlan(forceNewPlan); 491 if (assignCandidate != null) { 492 builder.setAssignCandidate(ProtobufUtil.toServerName(assignCandidate)); 493 } 494 serializer.serialize(builder.build()); 495 } 496 497 @Override 498 protected void deserializeStateData(ProcedureStateSerializer serializer) throws IOException { 499 super.deserializeStateData(serializer); 500 RegionStateTransitionStateData data = 501 serializer.deserialize(RegionStateTransitionStateData.class); 502 type = convert(data.getType()); 503 setInitalAndLastState(); 504 forceNewPlan = data.getForceNewPlan(); 505 if (data.hasAssignCandidate()) { 506 assignCandidate = ProtobufUtil.toServerName(data.getAssignCandidate()); 507 } 508 } 509 510 @Override 511 protected ProcedureMetrics getProcedureMetrics(MasterProcedureEnv env) { 512 MetricsAssignmentManager metrics = env.getAssignmentManager().getAssignmentManagerMetrics(); 513 switch (type) { 514 case ASSIGN: 515 return metrics.getAssignProcMetrics(); 516 case UNASSIGN: 517 return metrics.getUnassignProcMetrics(); 518 case MOVE: 519 return metrics.getMoveProcMetrics(); 520 case REOPEN: 521 return metrics.getReopenProcMetrics(); 522 default: 523 throw new IllegalArgumentException("Unknown transition type: " + type); 524 } 525 } 526 527 @Override 528 public void toStringClassDetails(StringBuilder sb) { 529 super.toStringClassDetails(sb); 530 if (initialState == RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE) { 531 sb.append(", ASSIGN"); 532 } else if (lastState == RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_CLOSED) { 533 sb.append(", UNASSIGN"); 534 } else { 535 sb.append(", REOPEN/MOVE"); 536 } 537 } 538 539 private static TransitRegionStateProcedure setOwner(MasterProcedureEnv env, 540 TransitRegionStateProcedure proc) { 541 proc.setOwner(env.getRequestUser().getShortName()); 542 return proc; 543 } 544 545 public enum TransitionType { 546 ASSIGN, UNASSIGN, MOVE, REOPEN 547 } 548 549 // Be careful that, when you call these 4 methods below, you need to manually attach the returned 550 // procedure with the RegionStateNode, otherwise the procedure will quit immediately without doing 551 // anything. See the comment in executeFromState to find out why we need this assumption. 552 public static TransitRegionStateProcedure assign(MasterProcedureEnv env, RegionInfo region, 553 @Nullable ServerName targetServer) { 554 return assign(env, region, false, targetServer); 555 } 556 557 public static TransitRegionStateProcedure assign(MasterProcedureEnv env, RegionInfo region, 558 boolean forceNewPlan, @Nullable ServerName targetServer) { 559 return setOwner(env, new TransitRegionStateProcedure(env, region, targetServer, forceNewPlan, 560 TransitionType.ASSIGN)); 561 } 562 563 public static TransitRegionStateProcedure unassign(MasterProcedureEnv env, RegionInfo region) { 564 return setOwner(env, 565 new TransitRegionStateProcedure(env, region, null, false, TransitionType.UNASSIGN)); 566 } 567 568 public static TransitRegionStateProcedure reopen(MasterProcedureEnv env, RegionInfo region) { 569 return setOwner(env, 570 new TransitRegionStateProcedure(env, region, null, false, TransitionType.REOPEN)); 571 } 572 573 public static TransitRegionStateProcedure move(MasterProcedureEnv env, RegionInfo region, 574 @Nullable ServerName targetServer) { 575 return setOwner(env, new TransitRegionStateProcedure(env, region, targetServer, 576 targetServer == null, TransitionType.MOVE)); 577 } 578}