2

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);
            }
            ));
Peter Csala
  • 17,736
  • 16
  • 35
  • 75
Rafael
  • 35
  • 5

2 Answers2

1

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.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • Is is possible to set polly policy for existed IHttpClientBuilder (when use other library)? – Rafael Aug 03 '23 at 12:13
  • @Rafael have not tried it. You can try to reregister that service with call `AddHttpClient` after the library register invocation. – Guru Stron Aug 03 '23 at 12:19
1

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
]
Peter Csala
  • 17,736
  • 16
  • 35
  • 75