docs: private void executeBatch(int batchId, final RebalanceBatchPlan batchPlan) {
          final Cluster batchCurrentCluster = batchPlan.getCurrentCluster();
          final List<StoreDefinition> batchCurrentStoreDefs = batchPlan.getCurrentStoreDefs();
          final Cluster batchFinalCluster = batchPlan.getFinalCluster();
          final List<StoreDefinition> batchFinalStoreDefs = batchPlan.getFinalStoreDefs();
  
          try {
              final List<RebalanceTaskInfo> rebalanceTaskInfoList = batchPlan.getBatchPlan();
  
              if(rebalanceTaskInfoList.isEmpty()) {
                  RebalanceUtils.printBatchLog(batchId, logger, "Skipping batch "
                                                                + batchId + " since it is empty.");
                  // Even though there is no rebalancing work to do, cluster
                  // metadata must be updated so that the server is aware of the
                  // new cluster xml.
                  adminClient.rebalanceOps.rebalanceStateChange(batchCurrentCluster,
                                                                batchFinalCluster,
                                                                batchCurrentStoreDefs,
                                                                batchFinalStoreDefs,
                                                                rebalanceTaskInfoList,
                                                                false,
                                                                true,
                                                                false,
                                                                false,
                                                                true);
                  return;
              }
  
              RebalanceUtils.printBatchLog(batchId, logger, "Starting batch "
                                                            + batchId + ".");
  
              // Split the store definitions
              List<StoreDefinition> readOnlyStoreDefs = StoreDefinitionUtils.filterStores(batchFinalStoreDefs,
                                                                                          true);
              List<StoreDefinition> readWriteStoreDefs = StoreDefinitionUtils.filterStores(batchFinalStoreDefs,
                                                                                           false);
              boolean hasReadOnlyStores = readOnlyStoreDefs != null
                                          && readOnlyStoreDefs.size() > 0;
              boolean hasReadWriteStores = readWriteStoreDefs != null
                                           && readWriteStoreDefs.size() > 0;
  
              // STEP 1 - Cluster state change
              boolean finishedReadOnlyPhase = false;
              List<RebalanceTaskInfo> filteredRebalancePartitionPlanList = RebalanceUtils.filterTaskPlanWithStores(rebalanceTaskInfoList,
                                                                                                                   readOnlyStoreDefs);
  
              rebalanceStateChange(batchId,
                                   batchCurrentCluster,
                                   batchCurrentStoreDefs,
                                   batchFinalCluster,
                                   batchFinalStoreDefs,
                                   filteredRebalancePartitionPlanList,
                                   hasReadOnlyStores,
                                   hasReadWriteStores,
                                   finishedReadOnlyPhase);
  
              // STEP 2 - Move RO data
              if(hasReadOnlyStores) {
                  RebalanceBatchPlanProgressBar progressBar = batchPlan.getProgressBar(batchId);
                  executeSubBatch(batchId,
                                  progressBar,
                                  batchCurrentCluster,
                                  batchCurrentStoreDefs,
                                  filteredRebalancePartitionPlanList,
                                  hasReadOnlyStores,
                                  hasReadWriteStores,
                                  finishedReadOnlyPhase);
              }
  
              // STEP 3 - Cluster change state
              finishedReadOnlyPhase = true;
              filteredRebalancePartitionPlanList = RebalanceUtils.filterTaskPlanWithStores(rebalanceTaskInfoList,
                                                                                           readWriteStoreDefs);
  
              rebalanceStateChange(batchId,
                                   batchCurrentCluster,
                                   batchCurrentStoreDefs,
                                   batchFinalCluster,
                                   batchFinalStoreDefs,
                                   filteredRebalancePartitionPlanList,
                                   hasReadOnlyStores,
                                   hasReadWriteStores,
                                   finishedReadOnlyPhase);
  
              // STEP 4 - Move RW data
              if(hasReadWriteStores) {
                  proxyPause();
                  RebalanceBatchPlanProgressBar progressBar = batchPlan.getProgressBar(batchId);
                  executeSubBatch(batchId,
                                  progressBar,
                                  batchCurrentCluster,
                                  batchCurrentStoreDefs,
                                  filteredRebalancePartitionPlanList,
                                  hasReadOnlyStores,
                                  hasReadWriteStores,
                                  finishedReadOnlyPhase);
              }
  
              RebalanceUtils.printBatchLog(batchId,
                                           logger,
                                           "Successfully terminated batch "
                                                   + batchId + ".");
  
          } catch(Exception e) {
              RebalanceUtils.printErrorLog(batchId, logger, "Error in batch "
                                                            + batchId + " - " + e.getMessage(), e);
              throw new VoldemortException("Rebalance failed on batch " + batchId,
                                           e);
          }
      }