Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing Thread#sleep call in the ExponentialBackoffRetryStrategy implementation? #658

Open
cbismuth opened this issue Mar 9, 2020 · 1 comment

Comments

@cbismuth
Copy link

cbismuth commented Mar 9, 2020

Hi,

I may have missed something, but I can't see any Thread#sleep call in these implementations below.

And our production systems went down over the past week-end due to more than 22 000 requests with 429 HTTP status code.

while (retryStrategy.shouldRetry(tryCount, response)) {
tryCount++;
if (response.body() != null) {
response.body().close();
}
// retry the request
response = chain.proceed(request);
}

public boolean shouldRetry(int retryCount, Response response) {
int code = response.code();
//CHECKSTYLE IGNORE MagicNumber FOR NEXT 2 LINES
return retryCount < this.retryCount
&& (code == 408 || (code >= 500 && code != 501 && code != 505));
}

Thanks,
Christophe

@cbismuth cbismuth changed the title Missing Thread#sleep call in the ExponentialBackoffRetryStrategy implementation Missing Thread#sleep call in the ExponentialBackoffRetryStrategy implementation? Mar 9, 2020
@cbismuth
Copy link
Author

cbismuth commented Mar 9, 2020

Not sure it would be the best option, but here is what I would expect as an implementation.

final class ExponentialBackoffRetryStrategy extends RetryStrategy {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExponentialBackoffRetryStrategy.class);

    private final int maxRetryCount;
    private final long initialIntervalInMillis;
    private final long maxIntervalInMillis;
    private final double multiplier;

    ExponentialBackoffRetryStrategy(final int maxRetryCount,
                                    final int initialIntervalDuration,
                                    final TimeUnit initialIntervalTimeUnit,
                                    final int maxIntervalDuration,
                                    final TimeUnit maxIntervalTimeUnit,
                                    final double multiplier) {
        super(ExponentialBackoffRetryStrategy.class.getName(), false);

        this.maxRetryCount = maxRetryCount;
        this.multiplier = multiplier;

        initialIntervalInMillis = initialIntervalTimeUnit.toMillis(initialIntervalDuration);
        maxIntervalInMillis = maxIntervalTimeUnit.toMillis(maxIntervalDuration);
    }

    @Override
    public boolean shouldRetry(final int retryCount, final Response response) {
        final int code = response.code();

        final boolean isRetryable = code == HttpStatus.REQUEST_TIMEOUT.value() || code >= HttpStatus.INTERNAL_SERVER_ERROR.value()
                                                                                  && code != HttpStatus.NOT_IMPLEMENTED.value()
                                                                                  && code != HttpStatus.HTTP_VERSION_NOT_SUPPORTED.value();

        if (isRetryable && retryCount < maxRetryCount) {
            try {
                final long expected = (long) Math.floor((double) retryCount * (double) initialIntervalInMillis * multiplier);
                final long actual = Math.min(expected, maxIntervalInMillis);

                LOGGER.info("Status code [{}] received, sleeping [{}] ms before retrying ...", code, actual);

                Thread.sleep(actual);

                return true;
            } catch (final InterruptedException e) {
                LOGGER.warn("Can't cause the currently executing thread to sleep, retry attempt canceled");
            }
        }

        return false;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant