private void addOrUpdateInstanceInfos(Iterable<DataNodeConfig> dataNodeConfigs, String dcName) throws Exception {
      List<ReplicaId> totalAddedReplicas = new ArrayList<>();
      List<ReplicaId> totalRemovedReplicas = new ArrayList<>();
      for (DataNodeConfig dataNodeConfig : dataNodeConfigs) {
        Pair<List<ReplicaId>, List<ReplicaId>> addedAndRemovedReplicas;
        if (instanceNameToAmbryDataNode.containsKey(dataNodeConfig.getInstanceName())) {
          addedAndRemovedReplicas = updateInstanceInfo(dataNodeConfig, dcName);
        } else {
          addedAndRemovedReplicas = new Pair<>(createNewInstance(dataNodeConfig, dcName), new ArrayList<>());
        }
        totalAddedReplicas.addAll(addedAndRemovedReplicas.getFirst());
        totalRemovedReplicas.addAll(addedAndRemovedReplicas.getSecond());
      }
      // if this is not initial data node config change and any replicas are added or removed, we should invoke callbacks
      // for different clustermap change listeners (i.e replication manager, partition selection helper)
      logger.info(
          "DC {}: In total, {} replicas are being added and {} replicas are being removed. data node initialized: {}",
          dcName, totalAddedReplicas.size(), totalRemovedReplicas.size(), dataNodeConfigInitialized);
      if (dataNodeConfigInitialized && (!totalAddedReplicas.isEmpty() || !totalRemovedReplicas.isEmpty())) {
        for (ClusterMapChangeListener listener : clusterMapChangeListeners) {
          listener.onReplicaAddedOrRemoved(totalAddedReplicas, totalRemovedReplicas);
        }
      }
    }
Add new instances or update existing instances based on {@link DataNodeConfig}(s). This may also invoke callbacks
in some clustermap change listeners (i.e. {@link PartitionSelectionHelper}, ReplicationManager)
@param dataNodeConfigs the {@link DataNodeConfig}(s) used to update in-mem cluster map.
@param dcName data center name.
ambry-clustermap/src/main/java/com/github/ambry/clustermap/HelixClusterManager.java