Search code examples
c#sendgridhttp-status-codespollyretry-logic

How to set Polly Retry for the set specific StatusCodes only


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));

Solution

  • 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.