public Pair<ServerErrorCode, Boolean> isCaughtUp(DataNodeId dataNodeId, PartitionId partitionId,
      long acceptableLagInBytes, short numReplicasCaughtUpPerPartition) throws IOException, TimeoutException {
    AdminRequest adminRequest =
        new AdminRequest(AdminRequestOrResponseType.CatchupStatus, partitionId, correlationId.incrementAndGet(),
            CLIENT_ID);
    CatchupStatusAdminRequest catchupStatusRequest =
        new CatchupStatusAdminRequest(acceptableLagInBytes, numReplicasCaughtUpPerPartition, adminRequest);
    ResponseInfo response = sendRequestGetResponse(dataNodeId, partitionId, catchupStatusRequest);
    CatchupStatusAdminResponse adminResponse =
        CatchupStatusAdminResponse.readFrom(new NettyByteBufDataInputStream(response.content()));
    response.release();
    return new Pair<>(adminResponse.getError(),
        adminResponse.getError() == ServerErrorCode.NoError && adminResponse.isCaughtUp());
  }
Sends a {@link CatchupStatusAdminRequest} for {@code partitionIdStr} to {@code dataNodeId}.
@param dataNodeId the {@link DataNodeId} to contact.
@param partitionId the {@link PartitionId} to check catchup status for. If {@code null}, status is for all
                   partitions on {@code dataNodeId}
@param acceptableLagInBytes that lag in bytes that is considered OK.
@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.
@return the {@link ServerErrorCode} and the catchup status that is returned if the error code is
         {@link ServerErrorCode#NoError}, otherwise {@code false}.
@throws IOException
@throws TimeoutException
ambry-tools/src/main/java/com/github/ambry/tools/admin/ServerAdminTool.java