private void assignRegions(MasterProcedureEnv env, List<RegionInfo> regions)
    throws IOException, ProcedureSuspendedException {
    AssignmentManager am = env.getMasterServices().getAssignmentManager();
    boolean retainAssignment = env.getMasterConfiguration().getBoolean(MASTER_SCP_RETAIN_ASSIGNMENT,
      DEFAULT_MASTER_SCP_RETAIN_ASSIGNMENT);
    // Since we may suspend in the middle of this loop, so here we use processedRegions to record
    // the progress, so next time we can locate the correct region
    // We do not need to persist the processedRegions when serializing the procedure, as when master
    // restarts, the sub procedure list will be cleared when rescheduling this SCP again, so we need
    // to start from beginning.
    for (int n = regions.size(); processedRegions < n; processedRegions++) {
      RegionInfo region = regions.get(processedRegions);
      RegionStateNode regionNode = am.getRegionStates().getOrCreateRegionStateNode(region);
      // There are two possible ways where we have already hold the lock here
      // 1. We have already hold the lock and we suspend while updating meta, so after being woken
      // up, we should skip lock again.
      // 2. We suspend the procedure while trying to hold the lock, and finally it is our turn to
      // hold the lock and we schedule the procedure again, this time we should have already hold
      // the lock, so we do not need to lock again
      if (!regionNode.isLockedBy(this)) {
        regionNode.lock(this, () -> ProcedureFutureUtil.wakeUp(this, env));
      }
      try {
        if (
          ProcedureFutureUtil.checkFuture(this, this::getUpdateMetaFuture,
            this::setUpdateMetaFuture, () -> {
            })
        ) {
          continue;
        }
        // This is possible, as when a server is dead, TRSP will fail to schedule a RemoteProcedure
        // and then try to assign the region to a new RS. And before it has updated the region
        // location to the new RS, we may have already called the am.getRegionsOnServer so we will
        // consider the region is still on this crashed server. Then before we arrive here, the
        // TRSP could have updated the region location, or even finished itself, so the region is
        // no longer on this crashed server any more. We should not try to assign it again. Please
        // see HBASE-23594 for more details.
        // UPDATE: HBCKServerCrashProcedure overrides isMatchingRegionLocation; this check can get
        // in the way of our clearing out 'Unknown Servers'.
        if (!isMatchingRegionLocation(regionNode)) {
          // See HBASE-24117, though we have already changed the shutdown order, it is still worth
          // double checking here to confirm that we do not skip assignment incorrectly.
          if (!am.isRunning()) {
            throw new DoNotRetryIOException(
              "AssignmentManager has been stopped, can not process assignment any more");
          }
          LOG.info("{} found {} whose regionLocation no longer matches {}, skipping assign...",
            this, regionNode, serverName);
          continue;
        }
        if (regionNode.getProcedure() != null) {
          LOG.info("{} found RIT {}; {}", this, regionNode.getProcedure(), regionNode);
          ProcedureFutureUtil.suspendIfNecessary(this, this::setUpdateMetaFuture, regionNode
            .getProcedure().serverCrashed(env, regionNode, getServerName(), !retainAssignment), env,
            () -> {
            });
          continue;
        }
        if (
          env.getMasterServices().getTableStateManager().isTableState(regionNode.getTable(),
            TableState.State.DISABLED)
        ) {
          // This should not happen, table disabled but has regions on server.
          LOG.warn("Found table disabled for region {}, procDetails: {}", regionNode, this);
          continue;
        }
        TransitRegionStateProcedure proc =
          TransitRegionStateProcedure.assign(env, region, !retainAssignment, null);
        regionNode.setProcedure(proc);
        // It is OK to still use addChildProcedure even if we suspend in the middle of this loop, as
        // the subProcList will only be cleared when we successfully returned from the
        // executeFromState method. This means we will submit all the TRSPs after we successfully
        // finished this loop
        addChildProcedure(proc);
      } finally {
        if (updateMetaFuture == null) {
          regionNode.unlock(this);
        }
      }
    }
    // we will call this method two times if the region server carries meta, so we need to reset it
    // to 0 after successfully finished the above loop
    processedRegions = 0;
  }
Assign the regions on the crashed RS to other Rses.
<p/>
In this method we will go through all the RegionStateNodes of the give regions to find out
whether there is already an TRSP for the region, if so we interrupt it and let it retry on
other server, otherwise we will schedule a TRSP to bring the region online.
<p/>
We will also check whether the table for a region is enabled, if not, we will skip assigning
it.
hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ServerCrashProcedure.java