docs: private List<RebalanceTaskInfo> constructBatchPlan() {
          // Construct all store routing plans once.
          HashMap<String, StoreRoutingPlan> currentStoreRoutingPlans = new HashMap<String, StoreRoutingPlan>();
          for(StoreDefinition storeDef: currentStoreDefs) {
              currentStoreRoutingPlans.put(storeDef.getName(), new StoreRoutingPlan(currentCluster,
                                                                                    storeDef));
          }
          HashMap<String, StoreRoutingPlan> finalStoreRoutingPlans = new HashMap<String, StoreRoutingPlan>();
          for(StoreDefinition storeDef: finalStoreDefs) {
              finalStoreRoutingPlans.put(storeDef.getName(), new StoreRoutingPlan(finalCluster,
                                                                                  storeDef));
          }
  
          RebalanceTaskInfoBuilder rpiBuilder = new RebalanceTaskInfoBuilder();
          // For every node in the final cluster ...
          for(Node stealerNode: finalCluster.getNodes()) {
              int stealerZoneId = stealerNode.getZoneId();
              int stealerNodeId = stealerNode.getId();
  
              // Consider all store definitions ...
              for(StoreDefinition storeDef: finalStoreDefs) {
                  StoreRoutingPlan currentSRP = currentStoreRoutingPlans.get(storeDef.getName());
                  StoreRoutingPlan finalSRP = finalStoreRoutingPlans.get(storeDef.getName());
                  for(int stealerPartitionId: finalSRP.getZoneNAryPartitionIds(stealerNodeId)) {
                      // ... and all nary partition-stores,
                      // now steal what is needed
  
                      // Optimization for RW stores: Do not steal a partition-store you already host!
                      if (!storeDef.getType().equalsIgnoreCase(ReadOnlyStorageConfiguration.TYPE_NAME)) {
                          if (currentSRP.getReplicationNodeList(stealerPartitionId)
                                        .contains(stealerNodeId)) {
                              continue;
                          }
                      }
                      // Determine which node to steal from. This will find node that hosts the appropriate zone-nary
                      int donorNodeId = getDonorId(currentSRP,
                                                   finalSRP,
                                                   stealerZoneId,
                                                   stealerNodeId,
                                                   stealerPartitionId);
                      // Make sure that both donor and stealer aren't the same node
                      // Since optimization on line 275 wouldn't apply for RO stores, there is a chance that donorNodeId 
                      // and stealerNodeId are the same, hence this check is needed for RO stores
                      if (donorNodeId != stealerNodeId) {
                          rpiBuilder.addPartitionStoreMove(stealerNodeId,
                                                           donorNodeId,
                                                           storeDef.getName(),
                                                           stealerPartitionId);
                      }
                  }
              }
          }
  
          return rpiBuilder.buildRebalanceTaskInfos();
      }