private void throwOrDelay(Throwable e, String actionName, String partitionPath, int attempts)
      throws CloudStorageException {
    if (e instanceof CloudStorageException) {
      CloudStorageException cse = (CloudStorageException) e;
      int statusCode = cse.getStatusCode();
      String cause = cse.getCause() != null ? cse.getCause().getClass().getSimpleName() : "Unknown";
      if (cse.isRetryable() && attempts < maxAttempts) {
        long delay = (cse.getRetryDelayMs() > 0) ? cse.getRetryDelayMs() : defaultRetryDelay;
        logger.debug("{} failed partition {} statusCode {} cause {} attempt {}, retrying after {} ms.", actionName,
            partitionPath, statusCode, cause, attempts, delay);
        try {
          Thread.sleep(delay);
        } catch (InterruptedException iex) {
        }
        vcrMetrics.retryCount.inc();
        vcrMetrics.retryWaitTimeMsec.inc(delay);
      } else {
        // Either not retryable or exhausted attempts.
        Throwable t = cse.getCause();
        Set<StoreErrorCodes> errorCodes = new HashSet<>(
            Arrays.asList(StoreErrorCodes.AlreadyUpdated, StoreErrorCodes.IDDeleted,
                StoreErrorCodes.LifeVersionConflict, StoreErrorCodes.IDUndeleted));
        if (t instanceof StoreException && errorCodes.contains(((StoreException)t).getErrorCode())) {
          // Not very useful to log error here as they are printed in several other places
          logger.trace("{} failed partition {} statusCode {} cause {} made {} attempts.", actionName, partitionPath,
              statusCode, cause, attempts);
        } else {
          logger.error("{} failed partition {} statusCode {} cause {} made {} attempts.", actionName, partitionPath,
              statusCode, cause, attempts);
        }
        throw cse;
      }
    } else {
      // Unexpected exception
      throw new CloudStorageException(
          actionName + " failed " + (Utils.isNullOrEmpty(partitionPath) ? "." : "partition " + partitionPath), e);
    }
  }
Utility to either throw the input exception or sleep for a specified retry delay.
@param e the input exception to check.
@param actionName the name of the action that threw the exception.
@param partitionPath the partition acted on.
@param attempts the number of attempts made so far.
@throws CloudStorageException in case of any error.
ambry-cloud/src/main/java/com/github/ambry/cloud/CloudRequestAgent.java