I'm trying to use Microsoft.Extensions.Http.Polly
policy handler for retries when TaskCanceledException
or http transient errors, but it's not working properly.
I wrote the minimal API below just for testing:
Program.cs
using Polly;using Polly.Extensions.Http;using Polly.Retry;WebApplicationBuilder builder = WebApplication.CreateBuilder(args);AsyncRetryPolicy<HttpResponseMessage>? policy = HttpPolicyExtensions.HandleTransientHttpError() .Or<TaskCanceledException>() .WaitAndRetryAsync(5, _ => TimeSpan.FromSeconds(5));builder.Services.AddHttpClient("test", client => { client.BaseAddress = new Uri("http://localhost:5000"); client.Timeout = TimeSpan.FromSeconds(2); }) .AddPolicyHandler(policy);WebApplication app = builder.Build();app.MapPost("/status/{code}", async (string code, IHttpClientFactory httpClientFactory, CancellationToken cancellationToken) => { HttpClient client = httpClientFactory.CreateClient("test"); HttpResponseMessage response = await client.GetAsync($"/status/{code}", cancellationToken); response.EnsureSuccessStatusCode(); return response.Content.ReadAsStringAsync(cancellationToken); });app.MapPost("/timeout/{timeout:int}", async (int timeout, IHttpClientFactory httpClientFactory, CancellationToken cancellationToken) => { HttpClient client = httpClientFactory.CreateClient("test"); HttpResponseMessage response = await client.GetAsync($"/timeout/{timeout}", cancellationToken); response.EnsureSuccessStatusCode(); return response.Content.ReadAsStringAsync(cancellationToken); });#region Simulated APIapp.MapGet("/status/{code:int}", (int code) => Results.StatusCode(code));app.MapGet("/timeout/{timeout:int}", async (int timeout) =>{ await Task.Delay(TimeSpan.FromSeconds(timeout)); return Results.Ok();});#endregionapp.Run();
After calling POST http://localhost:5000/timeout/10
it raises this exception without any further attempts:
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]An unhandled exception has occurred while executing the request.System.Threading.Tasks.TaskCanceledException: The request was canceled due to the configured HttpClient.Timeout of 2 seconds elapsing.---> System.TimeoutException: A task was canceled.---> System.Threading.Tasks.TaskCanceledException: A task was canceled. at Polly.Retry.AsyncRetryEngine.ImplementationAsync[TResult](Func`3 action, Context context, CancellationToken cancellationToken, ExceptionPredicates shouldRetryExceptionPredicates, ResultPredicates`1 shouldRetryResultPredicates, Func`5 onRetryAsync, Int32 permittedRetryCount, IEnumerable`1 sleepDurationsEnumerable, Func`4 sleepDurationProvider, Boolean continueOnCapturedContext) at Polly.AsyncPolicy`1.ExecuteAsync(Func`3 action, Context context, CancellationToken cancellationToken, Boolean continueOnCapturedContext) at Microsoft.Extensions.Http.PolicyHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.<>c__DisplayClass5_0.<<SendCoreAsync>g__Core|0>d.MoveNext()--- End of stack trace from previous location --- at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken) --- End of inner exception stack trace --- --- End of inner exception stack trace --- at System.Net.Http.HttpClient.HandleFailure(Exception e, Boolean telemetryStarted, HttpResponseMessage response, CancellationTokenSource cts, CancellationToken cancellationToken, CancellationTokenSource pendingRequestsCts) at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken) at Program.<>c.<<<Main>$>b__0_3>d.MoveNext() in Z:\Repositories\POCs\PollyRetryTest\PollyRetryTest\Program.cs:line 35--- End of stack trace from previous location --- at Microsoft.AspNetCore.Http.RequestDelegateFactory.<ExecuteTaskOfT>g__ExecuteAwaited|129_0[T](Task`1 task, HttpContext httpContext, JsonTypeInfo`1 jsonTypeInfo) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
What am I doing wrong? Do I have to override the primary handler?