void handleDataNodeConfigChange(Iterable<DataNodeConfig> configs, String dcName, String sourceHelixClusterName) {
      try {
        synchronized (notificationLock) {
          if (!dataNodeConfigInitialized) {
            logger.info(
                "Received initial notification for data node config change from helix cluster {} in datacenter {}",
                sourceHelixClusterName, dcName);
          } else {
            logger.info("Data node config change triggered from helix cluster {} in datacenter {}",
                sourceHelixClusterName, dcName);
          }

          if (logger.isDebugEnabled()) {
            logger.debug("Detailed data node config from helix cluster {} in datacenter {} is: {}",
                sourceHelixClusterName, dcName, configs);
          }
          try {
            addOrUpdateInstanceInfos(configs, dcName);
            List<DataNodeConfig> dataNodeConfigs = new ArrayList<>();
            configs.forEach(dataNodeConfigs::add);
            for (ClusterMapChangeListener listener : clusterMapChangeListeners) {
              listener.onDataNodeConfigChange(dataNodeConfigs);
            }
          } catch (Exception e) {
            if (!dataNodeConfigInitialized) {
              logger.error("Exception occurred when initializing data nodes from helix cluster {} in datacenter {}: ",
                  sourceHelixClusterName, dcName, e);
              onInitializationFailure.accept(e);
            } else {
              logger.error(
                  "Exception occurred at runtime when handling data node config changes from helix cluster {} in datacenter {}: ",
                  sourceHelixClusterName, dcName, e);
              helixClusterManagerMetrics.instanceConfigChangeErrorCount.inc();
            }
          } finally {
            dataNodeConfigInitialized = true;
          }
          long counter = sealedStateChangeCounter.incrementAndGet();
          logger.trace("SealedStateChangeCounter increase to {}", counter);
          helixClusterManagerMetrics.dataNodeConfigChangeTriggerCount.inc();
        }
      } catch (Throwable t) {
        errorCount.incrementAndGet();
        throw t;
      }
    }
Handle any {@link DataNodeConfig} related change in current datacenter. Several events will trigger datanode config
change: (1) replica's seal or stop state has changed; (2) new node or new partition is added; (3) new replica is
added to existing node; (4) old replica is removed from existing node.
@param configs all the {@link DataNodeConfig}(s) in current data center. (Note that PreFetch is enabled by default
               in Helix)
@param dcName data center name.
@param sourceHelixClusterName name of helix cluster from which the data node configs are obtained.
ambry-clustermap/src/main/java/com/github/ambry/clustermap/HelixClusterManager.java