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

Support Retry-After HTTP header #104

Open
tmysik opened this issue Feb 6, 2018 · 10 comments
Open

Support Retry-After HTTP header #104

tmysik opened this issue Feb 6, 2018 · 10 comments

Comments

@tmysik
Copy link

tmysik commented Feb 6, 2018

It would be great if Spring Retry could support Retry-After HTTP header. Currently, this is not possible because one gets this header (and its value) from the response itself. In other words, it would be nice to have a way to set BackOff policy with a possibility to adjust it during retrying based on the value of the Retry-After header.

@dsyer
Copy link
Member

dsyer commented Feb 20, 2018

Interesting idea. Spring Retry has no dependency on HTTP though. So you would have to implement it as a RestTemplate interceptor (for example - same approach would work for other HTTP client libraries) that accesses the RetryContext via the RetrySynchronizationManager.

@tmysik
Copy link
Author

tmysik commented Feb 20, 2018

@dsyer

Sure, that makes sense. Do I understand it correctly that it should be possible even now? Or is there anything needed to be done in Spring Retry itself?

Thank you.

@dsyer
Copy link
Member

dsyer commented Feb 20, 2018

You might need to provide a custom BackoffPolicy or RetryPolicy (not sure). But I think it's doable with the retry context as it is already implemented.

@tmysik
Copy link
Author

tmysik commented Feb 20, 2018

@dsyer

Thanks, will have a look at it.

@bkrzyzanski
Copy link

@tmysik

Hey, how it ended up?

@tmysik
Copy link
Author

tmysik commented Jun 6, 2019

@bkrzyzanski

Thread.sleep()

@bkrzyzanski
Copy link

@tmysik

Did you do it in your custom Backoff policy?

@tmysik
Copy link
Author

tmysik commented Jun 6, 2019

@bkrzyzanski

No.

@bkrzyzanski
Copy link

@tmysik

Okay, I guess your intention isn't to be helpful.

@julien-may
Copy link

i had the same requirement and did a basic implementation for the Retry-After header to support seconds (a date is not supported). This is what i came up with:

@Slf4j
@RequiredArgsConstructor
public class HttpRetryAfterBackOffPolicy implements BackOffPolicy {
	public static final long DEFAULT_BACK_OFF_PERIOD = 1000L;

	@Setter
	private long defaultBackOffPeriod = DEFAULT_BACK_OFF_PERIOD;

	@Setter
	private Sleeper sleeper = new ThreadWaitSleeper();

	@Override
	public BackOffContext start(RetryContext context) {
		return new RetryAfterBackOffContext(context);
	}

	@Override
	public void backOff(BackOffContext backOffContext) throws BackOffInterruptedException {
		final RetryAfterBackOffContext context = (RetryAfterBackOffContext)backOffContext;

		final Long backOffPeriod = tryGetBackOffPeriod(context.getRetryContext().getLastThrowable())
			.orElse(this.defaultBackOffPeriod);

		try {
			sleeper.sleep(backOffPeriod);
		} catch (InterruptedException e) {
			throw new BackOffInterruptedException("Thread interrupted while sleeping", e);
		}
	}

	private Optional<Long> tryGetBackOffPeriod(Throwable throwable) {
		return throwable instanceof HttpClientErrorException.TooManyRequests
			? tryGetRetryAfterHeaderValue((HttpClientErrorException.TooManyRequests)throwable)
				.map(TimeUnit.SECONDS::toMillis)
			: Optional.empty();
	}

	private Optional<Long> tryGetRetryAfterHeaderValue(HttpClientErrorException.TooManyRequests tooManyRequests) {
		final HttpHeaders responseHeaders = tooManyRequests.getResponseHeaders();

		if (responseHeaders != null) {
			final List<String> values = responseHeaders.get(HttpHeaders.RETRY_AFTER);

			if (values != null && !values.isEmpty()) {
				try {
					return Optional.of(Long.valueOf(values.get(0)));
				} catch (NumberFormatException e) {
					log.warn(e.getMessage(), e);
				}
			}
		}

		return Optional.empty();
	}

	@Data
	@RequiredArgsConstructor
	private static class RetryAfterBackOffContext implements BackOffContext {
		private final RetryContext retryContext;
	}
}

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

No branches or pull requests

4 participants