private void addConfigNodeLocationsToReq(int dataNodeId, TDataNodeHeartbeatReq req) {
    Set<TEndPoint> confirmedConfigNodes = loadCache.getConfirmedConfigNodeEndPoints(dataNodeId);
    Set<TEndPoint> actualConfigNodes =
        getNodeManager().getRegisteredConfigNodes().stream()
            .map(TConfigNodeLocation::getInternalEndPoint)
            .collect(Collectors.toSet());
    /*
      In most cases, comparing actualConfigNodes and confirmedConfigNodes is sufficient, but in some cases it's not, hence the need for periodic sending.
      Here's an example:
      1. There are ConfigNode A and B, and one DataNode in the cluster. DataNode persists "ConfigNode list = [A,B]".
      2. ConfigNode B is removed. DataNode persists "ConfigNode list = [A]" but fails to confirm it to ConfigNode.
      3. ConfigNode B is re-added to the cluster.
      4. At this point, because actualConfigNodes and confirmedConfigNodes are identical, the ConfigNode list is not re-sent to the DataNode.
    */
    if (!actualConfigNodes.equals(confirmedConfigNodes)
        || heartbeatCounter.get() % configNodeListPeriodicallySyncInterval == 0) {
      req.setConfigNodeEndPoints(actualConfigNodes);
    }
  }
loop body of the heartbeat thread.
iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/service/HeartbeatService.java