private void balanceRegionLeader(
      TConsensusGroupType regionGroupType, String consensusProtocolClass) {
    // Collect the latest data and generate the optimal leader distribution
    Map<TConsensusGroupId, Integer> currentLeaderMap =
        getLoadManager().getLoadCache().getRegionLeaderMap(regionGroupType);
    Map<TConsensusGroupId, Integer> optimalLeaderMap =
        leaderBalancer.generateOptimalLeaderDistribution(
            getLoadManager().getLoadCache().getCurrentDatabaseRegionGroupMap(regionGroupType),
            getLoadManager().getLoadCache().getCurrentRegionLocationMap(regionGroupType),
            currentLeaderMap,
            getLoadManager().getLoadCache().getCurrentDataNodeStatisticsMap(),
            getLoadManager().getLoadCache().getCurrentRegionStatisticsMap(regionGroupType));

    // Transfer leader to the optimal distribution
    long currentTime = System.nanoTime();
    AtomicInteger requestId = new AtomicInteger(0);
    DataNodeAsyncRequestContext<TRegionLeaderChangeReq, TRegionLeaderChangeResp> clientHandler =
        new DataNodeAsyncRequestContext<>(CnToDnAsyncRequestType.CHANGE_REGION_LEADER);
    Map<TConsensusGroupId, ConsensusGroupHeartbeatSample> successTransferMap = new TreeMap<>();
    optimalLeaderMap.forEach(
        (regionGroupId, newLeaderId) -> {
          if (ConsensusFactory.RATIS_CONSENSUS.equals(consensusProtocolClass)
              && currentTime - lastFailedTimeForLeaderBalance.getOrDefault(regionGroupId, 0L)
                  <= BALANCE_RATIS_LEADER_FAILED_INTERVAL_IN_NS) {
            return;
          }

          int oldLeaderId = currentLeaderMap.get(regionGroupId);
          if (newLeaderId != -1 && !newLeaderId.equals(oldLeaderId)) {
            LOGGER.info(
                "[LeaderBalancer] Try to change the leader of Region: {} to DataNode: {} ",
                regionGroupId,
                newLeaderId);
            switch (consensusProtocolClass) {
              case ConsensusFactory.IOT_CONSENSUS:
              case ConsensusFactory.SIMPLE_CONSENSUS:
                // For IoTConsensus or SimpleConsensus protocol, change
                // RegionRouteMap is enough
                successTransferMap.put(
                    regionGroupId, new ConsensusGroupHeartbeatSample(currentTime, newLeaderId));
                break;
              case ConsensusFactory.IOT_CONSENSUS_V2:
                // For IoTConsensusV2 protocol, change RegionRouteMap and execute flush on old
                // region leader
                successTransferMap.put(
                    regionGroupId, new ConsensusGroupHeartbeatSample(currentTime, newLeaderId));
                // Prepare data for flushOldLeader
                if (oldLeaderId != -1) {
                  lastBalancedOldLeaderId2RegionMap.compute(
                      oldLeaderId,
                      (k, v) -> {
                        if (v == null) {
                          List<String> value = new ArrayList<>();
                          value.add(String.valueOf(regionGroupId.getId()));
                          return value;
                        }
                        v.add(String.valueOf(regionGroupId.getId()));
                        return v;
                      });
                }
                break;
              case ConsensusFactory.RATIS_CONSENSUS:
              default:
                // For ratis protocol, the ConfigNode-leader will send a changeLeaderRequest to the
                // new
                // leader.
                // And the RegionRouteMap will be updated by Cluster-Heartbeat-Service later if
                // change
                // leader success.
                // Force update region leader for ratis consensus when replication factor is 1.
                if (TConsensusGroupType.SchemaRegion.equals(regionGroupType)
                    && CONF.getSchemaReplicationFactor() == 1) {
                  successTransferMap.put(
                      regionGroupId, new ConsensusGroupHeartbeatSample(0, newLeaderId));
                } else if (TConsensusGroupType.DataRegion.equals(regionGroupType)
                    && CONF.getDataReplicationFactor() == 1) {
                  successTransferMap.put(
                      regionGroupId, new ConsensusGroupHeartbeatSample(0, newLeaderId));
                } else {
                  TDataNodeLocation newLeader =
                      getNodeManager().getRegisteredDataNode(newLeaderId).getLocation();
                  TRegionLeaderChangeReq regionLeaderChangeReq =
                      new TRegionLeaderChangeReq(regionGroupId, newLeader);
                  int requestIndex = requestId.getAndIncrement();
                  clientHandler.putRequest(requestIndex, regionLeaderChangeReq);
                  clientHandler.putNodeLocation(requestIndex, newLeader);
                }
                break;
            }
          }
        });
    if (requestId.get() > 0) {
      // Don't retry ChangeLeader request
      CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequest(clientHandler);
      for (int i = 0; i < requestId.get(); i++) {
        if (clientHandler.getResponseMap().get(i).getStatus().getCode()
            == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
          successTransferMap.put(
              clientHandler.getRequest(i).getRegionId(),
              new ConsensusGroupHeartbeatSample(
                  clientHandler.getResponseMap().get(i).getConsensusLogicalTimestamp(),
                  clientHandler.getRequest(i).getNewLeaderNode().getDataNodeId()));
        } else {
          lastFailedTimeForLeaderBalance.put(
              clientHandler.getRequest(i).getRegionId(), currentTime);
          LOGGER.error(
              "[LeaderBalancer] Failed to change the leader of Region: {} to DataNode: {}",
              clientHandler.getRequest(i).getRegionId(),
              clientHandler.getRequest(i).getNewLeaderNode().getDataNodeId());
        }
      }
    }

    getLoadManager().forceUpdateConsensusGroupCache(successTransferMap);

    // Prepare data for invalidSchemaCacheOfOldLeaders
    if (regionGroupType.equals(TConsensusGroupType.DataRegion)) {
      lastBalancedDataRegionSet = successTransferMap.keySet();
      lastDataRegion2OldLeaderMap = currentLeaderMap;
    }
  }
Balance cluster RegionGroup leader distribution through configured algorithm.
iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/RouteBalancer.java