Rafael
Rafael

Reputation: 45

How to filter specific endpoint for retry policy using Polly

How to filter specific endpoint for retry policy using Polly

All client requests MyServiceHttpClient will retry. How disable retry policy specific api?

services.AddHttpClient<MyServiceHttpClient>(client =>
{
    /* configuration */
})
.AddPolicyHandler((serviceProvider, request) => 
    HttpPolicyExtensions.HandleTransientHttpError()
        .WaitAndRetryAsync(3, 
            sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), 
            onRetry: (outcome, timespan, retryAttempt, context) =>
            {
                serviceProvider.GetService<ILogger<MyServiceHttpClient>>()
                    .LogWarning("Delaying for {delay}ms, then making retry {retry}.", timespan.TotalMilliseconds, retryAttempt);
            }
            ));

Upvotes: 3

Views: 971

Answers (2)

Peter Csala
Peter Csala

Reputation: 22829

Even though Guru Stron's proposed solution works it is pretty hard to maintain IMHO.

Let me present you an alternative solution. The WaitAndRetryAsync has many different overloads. Some requires a retryCount parameters, while others don't. If you use an overload which requires a IEnumerable<TimeSpan> retryDurations parameter then you can do the following:

.AddPolicyHandler((serviceProvider, request) =>
    HttpPolicyExtensions.HandleTransientHttpError()
        .WaitAndRetryAsync(
            GetRetryDelays(request), 
            (_, timespan, retryAttempt, __) =>
            {
                //logging               
            }));

Now lets see the related GetRetryDelays implementation

private static readonly Uri DoNotRetryUri = new("https://...");
private const int MaxRetryAttempt = 3;
private static IEnumerable<TimeSpan> GetRetryDelays(HttpRequestMessage request)
{
    if (request.RequestUri == DoNotRetryUri)
        return Array.Empty<TimeSpan>();

    return Enumerable.Range(0, MaxRetryAttempt)
        .Select(retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))
        .ToArray();
}
  • If the request uri is the same as the constant then it will not perform any retry
  • If the request uri is different than the constant then it create an array of sleep durations with the following values
[
   00:00:01
   ,
   00:00:02
   ,
   00:00:04
]

Upvotes: 1

Guru Stron
Guru Stron

Reputation: 143098

You can try using no-op policy:

builder.Services.AddHttpClient<MyServiceHttpClient>(client =>
    {
        /* configuration */
    })
    .AddPolicyHandler((serviceProvider, request) =>
        request.RequestUri.PathAndQuery.StartsWith("/api/") // your predicate
            ? Policy.NoOpAsync<HttpResponseMessage>() // <- no op for matching predicate
            : HttpPolicyExtensions.HandleTransientHttpError()
                .WaitAndRetryAsync(3,
                    sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
                    onRetry: (outcome, timespan, retryAttempt, context) =>
                    {
                        serviceProvider.GetService<ILogger<MyServiceHttpClient>>()
                            .LogWarning("Delaying for {delay}ms, then making retry {retry}.",
                                timespan.TotalMilliseconds, retryAttempt);
                    }
                ));

Or another approach would be to repeat the HandleTransientHttpError logic but with adding corresponding filter.

Upvotes: 2

Related Questions