public static <T> Callback<T> chainCallback(AsyncOperationTracker asyncOperationTracker, Callback<?> failureCallback,
      ThrowingConsumer<? super T> successAction) {
    asyncOperationTracker.markOperationStart();
    return (result, exception) -> {
      try {
        asyncOperationTracker.markOperationEnd();
        if (exception == null && successAction != null) {
          successAction.accept(result);
        }
      } catch (Throwable t) {
        asyncOperationTracker.markCallbackProcessingError();
        exception = t instanceof Exception ? (Exception) t
            : new RestServiceException(t, RestServiceErrorCode.InternalServerError);
      } finally {
        if (exception != null && failureCallback != null) {
          failureCallback.onCompletion(null, exception);
        }
        asyncOperationTracker.markCallbackProcessingEnd();
      }
    };
  }
Create a {@link Callback} that handles common tasks like updating metrics via a {@link AsyncOperationTracker} and
handling errors that may occur in the callback body. It is meant to be a part of multi-step callback chain and
handle intermediate failures in a consistent way.
@param <T> the result type that the callback accepts.
@param asyncOperationTracker the {@link AsyncOperationTracker} to use to update metrics for an async call. The
                             operation is marked as started right before the callback is constructed.
@param failureCallback the callback to call if this callback was called with an exception or if the
                       {@code successAction} call throws an exception.
@param successAction the action to take when the callback is called successfully. The result of the callback is
                     provided to this consumer. This consumer is allowed to throw an exception, in which case the
                     {@code failureCallback} will be called.
@return the managed {@link Callback}.
ambry-api/src/main/java/com/github/ambry/commons/CallbackUtils.java