public void transferRegionLeader(
      TConsensusGroupId regionId, TDataNodeLocation originalDataNode, TDataNodeLocation coodinator)
      throws ProcedureException, InterruptedException {
    // find new leader
    Optional<TDataNodeLocation> newLeaderNode = Optional.empty();
    List<TDataNodeLocation> excludeDataNode = new ArrayList<>();
    excludeDataNode.add(originalDataNode);
    excludeDataNode.add(coodinator);
    newLeaderNode = filterDataNodeWithOtherRegionReplica(regionId, excludeDataNode);
    if (!newLeaderNode.isPresent()) {
      // If we have no choice, we use it
      newLeaderNode = Optional.of(coodinator);
    }
    // ratis needs DataNode to do election by itself
    long timestamp = System.nanoTime();
    if (TConsensusGroupType.SchemaRegion.equals(regionId.getType())
        || TConsensusGroupType.DataRegion.equals(regionId.getType())
            && RATIS_CONSENSUS.equals(CONF.getDataRegionConsensusProtocolClass())) {
      final int MAX_RETRY_TIME = 10;
      int retryTime = 0;
      long sleepTime =
          (CONF.getSchemaRegionRatisRpcLeaderElectionTimeoutMaxMs()
                  + CONF.getSchemaRegionRatisRpcLeaderElectionTimeoutMinMs())
              / 2;
      Integer leaderId = configManager.getLoadManager().getRegionLeaderMap().get(regionId);

      if (leaderId != -1) {
        // The migrated node is not leader, so we don't need to transfer temporarily
        if (originalDataNode.getDataNodeId() != leaderId) {
          return;
        }
      }
      while (true) {
        TRegionLeaderChangeResp resp =
            SyncDataNodeClientPool.getInstance()
                .changeRegionLeader(
                    regionId, originalDataNode.getInternalEndPoint(), newLeaderNode.get());
        if (resp.getStatus().getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
          timestamp = resp.getConsensusLogicalTimestamp();
          break;
        }
        if (retryTime++ > MAX_RETRY_TIME) {
          LOGGER.warn("[RemoveRegion] Ratis transfer leader fail, but procedure will continue.");
          return;
        }
        LOGGER.warn(
            "Call changeRegionLeader fail for the {} time, will sleep {} ms", retryTime, sleepTime);
        Thread.sleep(sleepTime);
      }
    }

    configManager
        .getLoadManager()
        .forceUpdateConsensusGroupCache(
            Collections.singletonMap(
                regionId,
                new ConsensusGroupHeartbeatSample(timestamp, newLeaderNode.get().getDataNodeId())));
    configManager.getLoadManager().getRouteBalancer().balanceRegionLeaderAndPriority();

    LOGGER.info(
        "{}, Change region leader finished, regionId: {}, newLeaderNode: {}",
        REGION_MIGRATE_PROCESS,
        regionId,
        newLeaderNode);
  }
Change the leader of given Region.

<p>For IOT_CONSENSUS, using `changeLeaderForIoTConsensus` method to change the regionLeaderMap
maintained in ConfigNode.

<p>For RATIS_CONSENSUS, invoking `changeRegionLeader` DataNode RPC method to change the leader.

@param regionId The region to be migrated
@param originalDataNode The DataNode where the region locates
iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/env/RegionMaintainHandler.java