question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[already possible] Respect Retry-After HTTP header

See original GitHub issue

The Retry-After response header is used to indicate how long a client should wait before sending the next request. It would be great if RetryPolicy took this into account.

Pseudocode:

var serverWaitDuration = getServerWaitDuration(response.Headers.Get("Retry-After"));
var waitDuration = Math.Max(clientWaitDuration.TotalMilliseconds, serverWaitDuration.TotalMilliseconds);

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:12 (6 by maintainers)

github_iconTop GitHub Comments

3reactions
reisenbergercommented, Mar 9, 2018

Hi @nmurf! You can already do this with current Polly. You can use a wait-and-retry overload taking the returned response as input to the sleepDurationProvider delegate. Something like:

IAsyncPolicy<HttpResponseMessage> retryHonouringRetryAfter =
    Policy.Handle<HttpRequestException>
        .OrResult<HttpResponseMessage>(r => /* clauses for other status codes you want to handle */
            || r.StatusCode == (HttpStatusCode)429) // RetryAfter
        .WaitAndRetryAsync(
            retryCount: 3, 
            sleepDurationProvider: (retryCount, response, context) => {
                // taking the pseudocode from the question
                var serverWaitDuration = getServerWaitDuration(response.Result?.Headers.Get("Retry-After")); // explaining response.Result? : The response is a Polly type DelegateResult<HttpResponseMessage>. Use null defence, as response.Result will be null if the cause for retry was an exception (in which case there was on result).
                var waitDuration = Math.Max(clientWaitDuration.TotalMilliseconds, serverWaitDuration.TotalMilliseconds);
                return TimeSpan.FromMilliseconds(waitDuration); 
            },
            onRetryAsync: async (response, timespan, retryCount, context) => { 
                /* perhaps some logging, eg the retry count, the timespan delaying for */ 
            }
        );

This is pseudo-code (for speed of response) : you may have to vary some details. For example, Azure Cosmos DB client SDK will wrap this response up in an Exception with a RetryAfter property after making its own retries first. We’d love to see your final version!

This is documented in the Polly wiki here but I’ll aim to reference that also from the main Polly readme, to make it more discoverable. More info also in this similar question.

Let us know how you get on!

2reactions
nmurfcommented, Mar 9, 2018

Thanks for the quick response, @reisenberger! Great library – I did not notice the provider overload until you pointed it out.

For posterity, here is an implementation of the method to obtain the header value as a TimeSpan (assumes server is in UTC):

private TimeSpan getServerWaitDuration(DelegateResult<HttpResponseMessage> response)
{
	var retryAfter = response?.Result?.Headers?.RetryAfter;
	if (retryAfter == null)
		return TimeSpan.Zero;
	
	return retryAfter.Date.HasValue
		? retryAfter.Date.Value - DateTime.UtcNow
		: retryAfter.Delta.GetValueOrDefault(TimeSpan.Zero);
}

Here’s the rest:

var clientWaitDurations = DecorrelatedJitter(numRetries, firstWaitDuration.Value, maxWaitDuration.Value)
	.ToArray();

_policy = Policy
	.Handle<HttpRequestException>()
	.OrResult(filter)
	.WaitAndRetryAsync(
		retryCount: numRetries,
		sleepDurationProvider: (retryCount, response, context) => 
			TimeSpan.FromMilliseconds(
				Math.Max(
					clientWaitDurations[retryCount - 1].TotalMilliseconds, 
					getServerWaitDuration(response).TotalMilliseconds));
Read more comments on GitHub >

github_iconTop Results From Across the Web

Retry-After HTTP header in practice - Tomasz Nurkiewicz
The Retry-After response-header field can be used with a 503 (Service Unavailable) response to indicate how long the service is expected to ...
Read more >
Retry-After - HTTP - MDN Web Docs
The Retry-After response HTTP header indicates how long the user agent should wait before making a follow-up request.
Read more >
Retry-after HTTP response header - does it affect anything?
The Retry-After response-header field can be used with a 503 (Service Unavailable) response to indicate how long the service is expected to ...
Read more >
Retry-After HTTP Header in Practice
The Retry-After response-header field can be used with a 503 (Service Unavailable) response to indicate how long the service is expected to ...
Read more >
HTTP/1.1: Header Field Definitions
The Accept request-header field can be used to specify certain media types ... The special "*" symbol in an Accept-Encoding field matches any...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found