Repository: apache/hadoop
File: hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/ExponentialRetryPolicy.java (Java)

Code before:
   *                     between retries.
   */
  public ExponentialRetryPolicy(final int retryCount, final int minBackoff, final int maxBackoff, final int deltaBackoff) {
    this.retryCount = retryCount;
    this.minBackoff = minBackoff;
    this.maxBackoff = maxBackoff;
    this.deltaBackoff = deltaBackoff;
  }

  /**
   * Returns if a request should be retried based on the retry count, current response,
   * and the current strategy.
   * HTTP status code 410 qualifies for retry based on
   * https://docs.microsoft.com/en-in/azure/virtual-machines/linux/
   * instance-metadata-service?tabs=windows#errors-and-debugging
   *
   * @param retryCount The current retry attempt count.
   * @param statusCode The status code of the response, or -1 for socket error.
   * @return true if the request should be retried; false otherwise.
   */
  public boolean shouldRetry(final int retryCount, final int statusCode) {
    return retryCount < this.retryCount
        && (statusCode == -1
        || statusCode == HttpURLConnection.HTTP_CLIENT_TIMEOUT
        || statusCode == HttpURLConnection.HTTP_GONE
        || statusCode == HTTP_TOO_MANY_REQUESTS
        || (statusCode >= HttpURLConnection.HTTP_INTERNAL_ERROR
            && statusCode != HttpURLConnection.HTTP_NOT_IMPLEMENTED
            && statusCode != HttpURLConnection.HTTP_VERSION));
  }

  /**
   * Returns backoff interval between 80% and 120% of the desired backoff,
   * multiply by 2^n-1 for exponential.
   *
   * @param retryCount The current retry attempt count.
   * @return backoff Interval time
   */
  public long getRetryInterval(final int retryCount) {
    final long boundedRandDelta = (int) (this.deltaBackoff * MIN_RANDOM_RATIO)
        + this.randRef.nextInt((int) (this.deltaBackoff * MAX_RANDOM_RATIO)
        - (int) (this.deltaBackoff * MIN_RANDOM_RATIO));

    final double incrementDelta = (Math.pow(2, retryCount - 1)) * boundedRandDelta;

    final long retryInterval = (int) Math.round(Math.min(this.minBackoff + incrementDelta, maxBackoff));

    return retryInterval;
  }

  @VisibleForTesting

Review comment (security):
Should we keep this in AzureADAuthentication condition.

Reason being, now if any API in AbfsHttpOperation get 429 or 410, it will be retried 30 times.
Right now what would happen in 429 / 410:
`executeHttpOperation` would give true and in completeExecute after very first call:
```
 if (result.getStatusCode() >= HttpURLConnection.HTTP_BAD_REQUEST) {
      throw new AbfsRestOperationException(result.getStatusCode(), result.getStorageErrorCode(),
          result.getStorageErrorMessage(), null, result);
    }

```
would throw exception.

Code after:
   * @param retryCount The current retry attempt count.
   * @return backoff Interval time
   */
  @Override
  public long getRetryInterval(final int retryCount) {
    final long boundedRandDelta = (int) (this.deltaBackoff * MIN_RANDOM_RATIO)
        + this.randRef.nextInt((int) (this.deltaBackoff * MAX_RANDOM_RATIO)
        - (int) (this.deltaBackoff * MIN_RANDOM_RATIO));

    final double incrementDelta = (Math.pow(2, retryCount - 1)) * boundedRandDelta;

    final long retryInterval = (int) Math.round(Math.min(
            this.minBackoff + incrementDelta, maxBackoff));

    return retryInterval;
  }

  @VisibleForTesting
  int getMinBackoff() {
    return this.minBackoff;
  }

  @VisibleForTesting
  int getMaxBackoff() {
    return maxBackoff;
  }

  @VisibleForTesting
  int getDeltaBackoff() {
    return this.deltaBackoff;
  }

}