public <T> T executeWithRetry(@Nonnull RetryableOperation<T> operation,
      @Nonnull String operationName) throws Exception {
    Objects.requireNonNull(operation, "operation cannot be null");
    int attempts = 1;
    T result;

    while (true) {
      try {
        result = operation.execute();
        logger.info("{} succeeded after {} attempts", operationName, attempts);
        return result;
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt(); // Preserve the interrupt status
        logMessageAndThrow(operationName, attempts, maxRetries, e);
      } catch (Exception e) {
        attempts++;
        logMessageAndThrow(operationName, attempts, maxRetries, e);
      }

      try {
        logger.info("{} failed. Retrying in {} ms. re-attempt {}/{}", operationName, retryDelayInMS, attempts - 1, maxRetries);
        TimeUnit.MILLISECONDS.sleep(retryDelayInMS);
      } catch (InterruptedException sleepInterrupted) {
        Thread.currentThread().interrupt();
        logger.error("{} was interrupted during retry delay", operationName, sleepInterrupted);
        throw sleepInterrupted;
      }
    }
  }
Execute the operation with retries
@param operation the operation to execute of type {@link RetryableOperation<T>}
@param operationName the name of the operation. Needed for logging.
@return the result of the operation of type T
@param <T> the return type of the executable operation
@throws IOException
@throws ConnectionPoolTimeoutException
@throws InterruptedException
ambry-file-transfer/src/main/java/com/github/ambry/filetransfer/utils/OperationRetryHandler.java