Search code examples
c#.nettimeoutpollyretry-logic

How can I return 408 Request timeout to Polly so that it can apply retry policy?


I have the following client app

services
    .AddHttpClient<IOrderService, OrderService>(c =>
    {
        c.BaseAddress = new Uri(Configuration["ApiSettings:xxx"]);
    })
    .AddPolicyHandler(GetRetryPolicy())


private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return
        HttpPolicyExtensions
            .HandleTransientHttpError()
            .WaitAndRetryAsync(
                retryCount: 3,
                sleepDurationProvider: currNthRetry => TimeSpan.FromSeconds(Math.Pow(2, currNthRetry)),
                onRetry: (Exception, retryCount, context) =>
                {
                    // ...
                });

}

so when the server is down/off, my client app is just hung, polly doesn't apply retry policy, I think it is because Polly expects to receive http status code of 408 Request Timeout first, then it can retry, but the server is down, the server cannot generate 408 error code and sent it back to the for sure. so how can I set the default timeout for Polly?

I was trying to set timeout in HttpClient as:

services
    .AddHttpClient<IOrderService, OrderService>(c =>
    {
        // ...
        c.Timeout = TimeSpan.FromSeconds(5);
    })
    // ...

but it doesn't work, all I got is a TaskCanceledException as below picture shows:

enter image description here

and Polly still doesn't do any retry.

So how can I return a 408 status code for Polly to consume?


Solution

  • I want the client app to retry a request every 5 seconds for 3 times only, that's all. The problem I have is, the server cannot return 5XX or 4XX to the client since the server is down. so the HttpClient needs to generate 408 for Polly

    If your server is down and you can't get a response from it then you would either receive an HttpRequestException or you can have a 5 seconds timeout per request.

    The former case is handled by the HandleTransientHttpError since it handles HttpRequestException, Http status codes >= 500 (server errors) and status code 408 (request timeout).

    The latter case can be achieved by combining/chaining two policies.

    IAsyncPolicy<HttpResponseMessage> GetPolicy()
    {
        var timeout = Policy
            .TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(5));
        
        var retry = HttpPolicyExtensions
            .HandleTransientHttpError()
            .Or<TimeoutRejectedException>()
            .WaitAndRetryAsync(
                retryCount: 3,
                sleepDurationProvider: _ => TimeSpan.FromSeconds(Math.Pow(2, _)));
        
        return Policy.WrapAsync(retry, timeout);
    }
    
    • The inner policy will be your timeout, which will be applied for each of the retry attempts separately
    • The outer policy will be your retry, which is aware of the possible timeout (.Or<TimeoutRejectedException>())