@Override
  public void run() {
    try {
      logger.trace("Starting replica thread on Local node: {} Thread name: {}", dataNodeId, threadName);
      for (Map.Entry<DataNodeId, Set<RemoteReplicaInfo>> replicasToReplicateEntry : replicasToReplicateGroupedByNode.entrySet()) {
        logger.trace("Remote node: {} Thread name: {} ReplicasToReplicate: {}", replicasToReplicateEntry.getKey(),
            threadName, replicasToReplicateEntry.getValue());
      }
      logger.info("Begin iteration for thread {}", threadName);
      while (running) {
        // TODO: Replicate method performs one cycle of replication for every replica and faster replicas would be
        //  blocked by the slower replicas. There is an optimization we can do, which is let each replica runs replication
        //  independently without waiting for each other. This would make sure that slower replicas won't block faster
        //  replicas.
        replicate();
        lock.lock();
        try {
          if (running && allDisabled) {
            pauseCondition.await();
          }
        } catch (Exception e) {
          logger.error("Received interrupted exception during pause", e);
        } finally {
          lock.unlock();
        }
      }
    } finally {
      running = false;
      shutdownLatch.countDown();
    }
  }
Logs replication progress of local node against some remote node
@param remoteReplicaInfo remote replica information
@param exchangeMetadataResponse metadata information from remote node
ambry-replication/src/main/java/com/github/ambry/replication/ReplicaThread.java