private boolean waitTillCatchupOrTimeout(DataNodeId dataNodeId, long acceptableLagInBytes,
      short numReplicasCaughtUpPerPartition, long timeoutAtSecs, long checkRepeatDelaySecs)
      throws InterruptedException, IOException, TimeoutException {
    boolean isCaughtUp;
    do {
      isCaughtUp = serverAdminTool.isCaughtUp(dataNodeId, null, acceptableLagInBytes, numReplicasCaughtUpPerPartition)
          .getSecond();
      if (!isCaughtUp) {
        // sleep for a configured amount of time before trying again.
        time.sleep(TimeUnit.SECONDS.toMillis(checkRepeatDelaySecs));
      }
    } while (!isCaughtUp && time.seconds() < timeoutAtSecs);
    return isCaughtUp;
  }
Waits until the lag of all peers of {@code dataNodeId} for all partitions <=
{@code acceptableLagInBytes} or until time moves past {@code timeoutAtSecs}.
@param dataNodeId the {@link DataNodeId} to ask for peer lag details.
@param acceptableLagInBytes that lag in bytes that is considered "caught up".
@param numReplicasCaughtUpPerPartition the number of replicas that have to be within {@code acceptableLagInBytes}
                                       (per partition). The min of this value or the total count of replicas - 1 is
                                       considered.
@param timeoutAtSecs the absolute time (in secs) at which the wait is considered failed.
@param checkRepeatDelaySecs the number of seconds after which the catchup status will be checked again if the
                            previous check did not succeed.
@return if the server's peers are confirmed to be within {@code acceptableLagInBytes}. {@code false} otherwise.
@throws InterruptedException if there are any interruptions while waiting for catch up.
@throws IOException if there is any I/O error.
@throws TimeoutException if there are timeout errors while making network requests.
ambry-tools/src/main/java/com/github/ambry/tools/admin/SafeServerShutdownTool.java