Gabriel Whitehair
Gabriel Whitehair

Reputation: 23

Polly Http Circuit Breaker Break only on Timeout Exception

We have a vendor that goes down a lot. We no longer want the circuit to break when they pass us back 500 errors, etc.. We just want the circuit to break when there is a Timeout Exception.

IAsyncPolicy<HttpResponseMessage> circuitBreaker = HttpPolicyExtensions
    .HandleTransientHttpError()
    .OrResult(msg => msg.StatusCode == HttpStatusCode.RequestTimeout)
    .AdvancedCircuitBreakerAsync(
        failureThreshold: 0.25,
        samplingDuration: TimeSpan.FromSeconds(300),
        minimumThroughput: 50,
        durationOfBreak: TimeSpan.FromSeconds(20));

services.AddHttpClient<IFinancialDetailsService, FinancialDetailsService>()
        .AddPolicyHandler(circuitBreaker);

How can I do this via Polly? HandleTransientHttpError handles 5XX and 408 errors, but I don't see any other extension methods besides that and OrTransientHttpError which does the same thing.

Upvotes: -1

Views: 87

Answers (1)

Peter Csala
Peter Csala

Reputation: 22829

The HandleTransientHttpError handles 408, 5XX status codes and HttpRequestException. It is important to understand that HttpClient does not throw TimeoutException directly. Rather than it is wrapped inside a TaskCanceledException since .NET 5:

try
{
    var response = await client.GetAsync(downstreamServiceUrl);
}
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
{
   ...
}

For earlier .NET versions you have to examine the CancellationTokenSource to determine whether is it a cancelled or a timed out request.

var cts = new CancellationTokenSource();
try
{
    var response = await client.GetAsync(downstreamServiceUrl, cts.Token);
}
catch (TaskCanceledException ex) when (!cts.IsCancellationRequested)
{
    ...  
}

So, if you are using .NET 5 or above you have to add the following clause to your policy builder (before the .AdvanceCircuitBreakerAsync):

.Or<TaskCanceledException>(ex => ex.InnerException is TimeoutException)

If you are using earlier .NET version then:

.Or<TaskCanceledException>(_ => !cts.IsCancellationRequested)

Update #1

If you want to handle only timeout scenarios then you don't need to use the HttpPolicyExtensions.HandleTransientHttpError() method. Rather than you can define your policy simply like this:

IAsyncPolicy<HttpResponseMessage> circuitBreaker = Policy<HttpResponseMessage>
    .Handle<TaskCanceledException>(ex => ex.InnerException is TimeoutException)
    .AdvancedCircuitBreakerAsync(...

OR

IAsyncPolicy<HttpResponseMessage> circuitBreaker = Policy<HttpResponseMessage>
    .Handle<TaskCanceledException>(_ => !cts.IsCancellationRequested)
    .AdvancedCircuitBreakerAsync(...

Upvotes: 0

Related Questions