4

I have a couple of questions around similar concern: I'm making a call to EmailServer and using Polly Policy to WaitAndRetry. But I would like the Retry to happen only for specific timeouts: 5xx and 429 (and ignore the rest of the 4xx).

How to make it more elegant and also cover all of the 5xx possibility (as the HttpStatusCode below does not cover all of the 5xx and also does not cover 429)?

Currently I have my code as following (2 parts/options):

private readonly HttpStatusCode[] _retryHttpStatusCodesList =
    new[]
    {
        HttpStatusCode.InternalServerError,
        HttpStatusCode.NotImplemented,            
        HttpStatusCode.BadGateway,
        HttpStatusCode.ServiceUnavailable,
        HttpStatusCode.GatewayTimeout,
        HttpStatusCode.HttpVersionNotSupported,
        HttpStatusCode.RequestTimeout,
        HttpStatusCode.Unauthorized,
        HttpStatusCode.RequestEntityTooLarge
    };

OPTION 1:

   var waitAndRetryPolicy = Policy.Handle<Exception>()
                .OrResult<HttpResponseMessage>(r =>  _retryHttpStatusCodesList.Contains(r.StatusCode))
                .RetryAsync(2, (ex, retryCount) => { Console.WriteLine($"Retry count {retryCount}"); });

OPTION 2 (this one I cannot figure out how to put the check for StatusCode at all):

var waitAndRetryPolicy = Policy.Handle<Exception>()
            .WaitAndRetryAsync(_maxRetries, retryAttempt => TimeSpan.FromMinutes(Math.Pow(_waitMinutes, retryAttempt)), 
            (ex, timeSpan) => 
            { 
                Console.WriteLine($"Log Error {ex}"); 
            });

   SendGridClient client = new SendGridClient ();
   var response = await policy.ExecuteAsync (() => 
               client.SendEmailAsync(new SendGridMessage));
Peter Csala
  • 17,736
  • 16
  • 35
  • 75
KVN
  • 863
  • 1
  • 17
  • 35
  • The list of retriable statuses https://stackoverflow.com/questions/47680711/which-http-errors-should-never-trigger-an-automatic-retry – Michael Freidgeim Mar 03 '23 at 21:01
  • Use retryHttpStatusCodesList is the most explicit way to describe what you want to retry https://stackoverflow.com/questions/51770071/what-are-the-http-codes-to-automatically-retry-the-request/74627395#74627395 – Michael Freidgeim Mar 03 '23 at 22:39

2 Answers2

4

Instead of the dictionary, you could simply inspect the response code directly:

var retryPolicy = Policy
    .Handle<Exception>()
    .OrResult<Sendgrid.Response>(r =>
    {
        var statusCode = (int)r.StatusCode;
        return (statusCode >= 500 && statusCode <= 599) || statusCode == 429;
    })
    .RetryAsync(2, (ex, retryCount) => { Console.WriteLine($"Retry count {retryCount}"); });

In answer to your comment, you only need to replace the call to RetryAsync(...) with your code for WaitAndRetryAsync(...):

var waitAndRetryPolicy = Policy
    .Handle<Exception>()
    .OrResult<Sendgrid.Response>(r =>
    {
        var statusCode = (int)r.StatusCode;
        return (statusCode >= 500 && statusCode <= 599) || statusCode == 429;
    })
    .WaitAndRetryAsync(_maxRetries, retryAttempt => TimeSpan.FromMinutes(Math.Pow(_waitMinutes, retryAttempt)),
        (ex, timeSpan) =>
        {
            Console.WriteLine($"Log Error {ex}");
        });

Remember, Polly is a fluent policy builder. That means the methods on the PolicyBuilder<T> instance that you're calling (e.g. .Handle<Exception>() and .OrResult<Sendgrid.Response>()) are configuring that builder in order to create a Policy<T> with a call such as .WaitAndRetryAsync() or RetryAsync(). The configuration methods don't care what type of policy you want to create, so they can be used for any policy type.

John H
  • 14,422
  • 4
  • 41
  • 74
  • Thanks @John H, do you know how can I do it for my option #2 ? - as this would be my main option to work with – KVN Apr 03 '20 at 21:36
  • @KVN I've expanded my answer. See if that helps. – John H Apr 03 '20 at 21:48
  • Thanks @John H, sorry I made a mistake in my original post (I have changed it). The main difference in Option #2 - is that the Policy will be used for SendGridClient (not HttpClient). And trying to use current Policy - will throw me an error: "Cannot convert Task. into Task.<.Net.Http.HttpResponseMessage>". Solving this error, while still Retry only when those Codes are met, is my main concern for this option. – KVN Apr 03 '20 at 22:09
  • 1
    Actually, I think I have just figured out the solution. That part should be as following: .OnResult(...). Thanks John H for guiding! – KVN Apr 03 '20 at 22:12
  • No problem. :) You beat me to it. I'll update my answer to reflect the same. – John H Apr 03 '20 at 22:14
2

You can use provided by Polly AddTransientHttpErrorPolicy or .HandleTransientHttpError() method that handle HttpRequestException, HTTP 5xx, HTTP 408

From What HTTP error codes are retried by Polly (.Net) by default?

Polly with HttpClientFactory (using services.AddHttpClient(...).AddTransientHttpErrorPolicy(...) in StartUp in .Net Core)

There is .HandleTransientHttpError() method available via the Polly.Extensions.Http package also handles the same set of exceptions and status codes.

However their decision to retry all 5xx codes is arguable. E.g. why to retry 501 NotImplemented?

If you want to maintain your own list of retriable errors, see What are the http codes to automatically retry the request? and more detailed reverse question Which HTTP errors should never trigger an automatic retry?

Michael Freidgeim
  • 26,542
  • 16
  • 152
  • 170