private Flow confirmClosed(MasterProcedureEnv env, RegionStateNode regionNode)
    throws IOException {
    if (regionNode.isInState(State.CLOSED)) {
      retryCounter = null;
      if (lastState == RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_CLOSED) {
        // we are the last state, finish
        regionNode.unsetProcedure(this);
        return Flow.NO_MORE_STATE;
      }
      // This means we need to open the region again, should be a move or reopen
      setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE);
      return Flow.HAS_MORE_STATE;
    }
    if (regionNode.isInState(State.CLOSING)) {
      // This is possible, think the target RS crashes and restarts immediately, the close region
      // operation will return a NotServingRegionException soon, we can only recover after SCP takes
      // care of this RS. So here we throw an IOException to let upper layer to retry with backoff.
      setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_CLOSE);
      throw new HBaseIOException("Failed to close region");
    }
    // abnormally closed, need to reopen it, no matter what is the last state, see the comment in
    // confirmOpened for more details that why we need to reopen the region first even if we just
    // want to close it.
    // The only exception is for non-default replica, where we do not need to deal with recovered
    // edits. Notice that the region will remain in ABNORMALLY_CLOSED state, the upper layer need to
    // deal with this state. For non-default replica, this is usually the same with CLOSED.
    assert regionNode.isInState(State.ABNORMALLY_CLOSED);
    if (
      !RegionReplicaUtil.isDefaultReplica(getRegion())
        && lastState == RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_CLOSED
    ) {
      regionNode.unsetProcedure(this);
      return Flow.NO_MORE_STATE;
    }
    retryCounter = null;
    setNextState(RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE);
    return Flow.HAS_MORE_STATE;
  }
The procedure to deal with the state transition of a region. A region with a TRSP in place is
called RIT, i.e, RegionInTransition.
<p/>
It can be used to assign/unassign/reopen/move a region, and for
{@link #unassign(MasterProcedureEnv, RegionInfo)} and
{@link #reopen(MasterProcedureEnv, RegionInfo)}, you do not need to specify a target server, and
for {@link #assign(MasterProcedureEnv, RegionInfo, ServerName)} and
{@link #move(MasterProcedureEnv, RegionInfo, ServerName)}, if you want to you can provide a
target server. And for {@link #move(MasterProcedureEnv, RegionInfo, ServerName)}, if you do not
specify a targetServer, we will select one randomly.
<p/>
<p/>
The typical state transition for assigning a region is:

<pre>
GET_ASSIGN_CANDIDATE ------> OPEN -----> CONFIRM_OPENED
</pre>

Notice that, if there are failures we may go back to the {@code GET_ASSIGN_CANDIDATE} state to
try again.
<p/>
The typical state transition for unassigning a region is:

<pre>
CLOSE -----> CONFIRM_CLOSED
</pre>

Here things go a bit different, if there are failures, especially that if there is a server
crash, we will go to the {@code GET_ASSIGN_CANDIDATE} state to bring the region online first, and
then go through the normal way to unassign it.
<p/>
The typical state transition for reopening/moving a region is:

<pre>
CLOSE -----> CONFIRM_CLOSED -----> GET_ASSIGN_CANDIDATE ------> OPEN -----> CONFIRM_OPENED
</pre>

The retry logic is the same with the above assign/unassign.
<p/>
Notice that, although we allow specify a target server, it just acts as a candidate, we do not
guarantee that the region will finally be on the target server. If this is important for you, you
should check whether the region is on the target server after the procedure is finished.
</p>
Altenatively, for trying retaining assignments, the
<b>hbase.master.scp.retain.assignment.force</b> option can be used together with
<b>hbase.master.scp.retain.assignment</b>.
<p/>
When you want to schedule a TRSP, please check whether there is still one for this region, and
the check should be under the RegionStateNode lock. We will remove the TRSP from a
RegionStateNode when we are done, see the code in {@code reportTransition} method below. There
could be at most one TRSP for a give region.
hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/TransitRegionStateProcedure.java