I'm using Polly (Microsoft.Extensions.Http.Polly
) with .net core with this configuration (with an invalid URL , for testing) :
private static void RegisterServices()
{
var collection = new ServiceCollection();
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(2); // Timeout for an individual try
collection.AddHttpClient<INetworkService, NetworkService>(url=>
{
url.BaseAddress = new Uri("http://www.google.com:81"); //test bad url
})
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(timeoutPolicy); ;
_serviceProvider = collection.BuildServiceProvider();
}
Where GetRetryPolicy
is :
private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode != HttpStatusCode.OK)
.Or<TimeoutRejectedException>()
.Or<TaskCanceledException>()
.Or<OperationCanceledException>()
.WaitAndRetryAsync(3, retryAttempt =>
{
return TimeSpan.FromSeconds(2);
},
onRetry: (response, delay, retryCount, context) =>
{
Console.WriteLine($"______PollyAttempt_____ retryCount:{retryCount} ");
});
}
Output is :
_PollyAttempt retryCount:1
_PollyAttempt retryCount:2
_PollyAttempt retryCount:3
Exception : (TimeoutException) The delegate executed asynchronously through TimeoutPolicy did not complete within the timeout.
I want to send an email after the last attempt was failing.
Question:
How can I catch that final exception? Is there any built-in mechanism that allows me to know that Polly was failed ?
(my currently working code : https://pastebin.pl/view/a2566d51)
Let's start with a simple setup where you don't use Polly at all:
private static void RegisterServices()
{
var collection = new ServiceCollection();
collection.AddHttpClient<INetworkService, NetworkService>(sonol =>
{
sonol.BaseAddress = new Uri("http://www.google.com:81");
});
_serviceProvider = collection.BuildServiceProvider();
}
TaskCanceledException
. In other words HttpClient cancels the request because it did not receive any response.Now let's bend the HttpClient setup a bit:
private static void RegisterServices()
{
var collection = new ServiceCollection();
collection.AddHttpClient<INetworkService, NetworkService>(sonol =>
{
sonol.Timeout = TimeSpan.FromSeconds(3); // << NEW CODE
sonol.BaseAddress = new Uri("http://www.google.com:81");
});
_serviceProvider = collection.BuildServiceProvider();
}
TaskCanceledException
Now, comment out this timeout setup and let's wire up the timeout Policy:
private static void RegisterServices()
{
var collection = new ServiceCollection();
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(2);
collection.AddHttpClient<INetworkService, NetworkService>(sonol =>
{
//sonol.Timeout = TimeSpan.FromSeconds(3);
sonol.BaseAddress = new Uri("http://www.google.com:81");
})
.AddPolicyHandler(timeoutPolicy); // << NEW CODE
_serviceProvider = collection.BuildServiceProvider();
}
TimeoutRejectedException
.
InnerException
is the original TaskCanceledException
.And finally let's add retry policy:
private static void RegisterServices()
{
var collection = new ServiceCollection();
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(2);
collection.AddHttpClient<INetworkService, NetworkService>(sonol =>
{
//sonol.Timeout = TimeSpan.FromSeconds(3);
sonol.BaseAddress = new Uri("http://www.google.com:81");
})
.AddPolicyHandler(Policy.WrapAsync(GetRetryPolicy(), timeoutPolicy)); // << NEW CODE
//.AddPolicyHandler(timeoutPolicy);
_serviceProvider = collection.BuildServiceProvider();
}
TimeoutRejectedException
.
TaskCanceledException
.UPDATE: Capture the essence of the comments
Where is the point where I know that all attempts have failed?
When your Polly decorated HttpClient
throws the TimeoutRejectedException
, you can be sure that all attempts failed. So, you should wrap the GetAsync
with try-catch inside the typed client.
Should I check the exception to see that it's timeoutException?
In case of malformed url it will throw different exception. So, if you catch TimeoutRejectedException
that would mean either the downstream is not available or it is overloaded.
Do I need to catch first TimeoutRejectedException exception in order to recognize retires have failed?
From the consumer perspective there will be a single exception. The retry policy will throw it
Handle<>
or Or<>
calls they are treated as unhandled. This means without any retry the policy will throw that.In other words, TimeoutRejectedException
will be thrown if the client will not receive answer from the downstream system for a given time period. But it might also throw HttpRequestException
if there is some network issue.
HttpRequestException
.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With