pixel
pixel

Reputation: 155

Retry multiple times when http request times out using polly c#

My original intention was to retry multiple times when the request itself times out, such as when trying to communicate to another micro service which is down for a couple of seconds in the hope it is a temporary failure. If there is a simpler solution to do just that it would suffice. I decided to manually set timeout per retry using a wrapping policy, hoping to achieve the same result.

I've seen a similar solution not using httpclientfactory here Use a specific timeout connected to a retrypolicy, but it doesn't work for me.

My code looks like this:

services.AddHttpClient("retryclient", client =>
{
    client.Timeout = TimeSpan.FromSeconds(100);
}).AddPolicyHandler((servs, request) =>
    Policy.HandleResult<HttpResponseMessage>(r =>
        {
            return r.StatusCode == HttpStatusCode.NotFound; //(1)
        }).
.OrTransiesntHttpError().
WaitAndRetryAsync(5, retryAttempt => TimeSpan.FromSeconds(retryAttempt),
    onRetry: (exception, timespan, retryAttempt, context) =>
        { // using servs to log // });
}).WrapAsync(Policy.TimeoutAsync(1)));

I have checked that the retry policy works when I try to access a 'not-found' adress without wrapping the timeout policy and it works fine. I've also tried to use line (1) with the status code HttpStatusCode.RequestTimeout instead of not found for my case, but it doesn't work.

When I am using the wrapping and trying to access a service which is down it throws a Polly.Timeout.TimeoutRejectedException on the first try as I would have expected, but doesn't retry again. I couldn't figure a way to retry multiple time, either with a set timeout for each retry, or just when the request itself time outs not using a timeout policy.

Edit: After further reading https://cm.engineering/transient-timeouts-and-the-retry-rabbit-hole-net-4-5-f406cebbf194 it seems my problem is inside the policy handling I don't have access to the cancellation token of the HttpClient. I assume I can fix it by overriding the sendAsync method as shown in link. Is there an elegant way to do same while creating the factory?

Upvotes: 4

Views: 6433

Answers (1)

mountain traveller
mountain traveller

Reputation: 8156

The Polly documentation on HttpClientFactory covers this in a section labelled applying timeouts:

You may want the retry policy to retry if any individual try timed out. To do this, make the retry policy handle the TimeoutRejectedException which Polly's timeout policy throws.

So:

services.AddHttpClient("retryclient", client =>
{
    client.Timeout = TimeSpan.FromSeconds(100);
}).AddPolicyHandler((servs, request) =>
    Policy.HandleResult<HttpResponseMessage>(r =>
        {
            return r.StatusCode == HttpStatusCode.NotFound; //(1)
        }).
.OrTransientHttpError()
.Or<TimeoutRejectedException>() // ** ADDED **
.WaitAndRetryAsync(5, retryAttempt => TimeSpan.FromSeconds(retryAttempt),
    onRetry: (exception, timespan, retryAttempt, context) =>
        { // using servs to log // });
})
.WrapAsync(Policy.TimeoutAsync(1)));

Polly passes the CancellationToken to the internal .SendAsync() call correctly; no changes should be needed concerning that.

Upvotes: 10

Related Questions