I'm trying to implement a circuitbreaker for the first time, but it just won't work. The fallback policy works but I can't seem to reach the circuitbreaker. I've tried this in different versions, including a retry policy for the circuitbreaker, but it doesn't seem to matter. I'm sure it's something basic I missed.
Here is a simplified version of the code for test purposes:
var timeoutPolicy = Policy
.TimeoutAsync(
_settings.TimeoutWhenCallingApi,
TimeoutStrategy.Pessimistic
);
var circuitBreaker = Policy
.Handle<TimeoutRejectedException>()
.CircuitBreakerAsync(
_settings.ConsecutiveExceptionsAllowedBeforeBreaking,
_settings.DurationOfBreak
)
.WrapAsync(timeoutPolicy);
policy = Policy
.Handle<Exception>()
.FallbackAsync(
async cancellationToken => { Console.WriteLine("fallback triggered"); })
.WrapAsync(circuitBreaker);
await policy.ExecuteAsync(() => Task.Delay(-1));
The following code sample constructs the timeout, circuit-breaker and fallback policies essentially identical to the original code posted in your question.
using Polly;
using System;
using System.Threading.Tasks;
public class Program
{
public static async void Main() {
var timeoutPolicy = Policy
.TimeoutAsync(
TimeSpan.FromMilliseconds(10), // _settings.TimeoutWhenCallingApi,
Polly.Timeout.TimeoutStrategy.Pessimistic
);
var circuitBreaker = Policy
.Handle<Polly.Timeout.TimeoutRejectedException>()
.CircuitBreakerAsync(
1, // _settings.ConsecutiveExceptionsAllowedBeforeBreaking,
TimeSpan.FromSeconds(30) // _settings.DurationOfBreak
);
var circuitBreakerWrappingTimeout = circuitBreaker
.WrapAsync(timeoutPolicy);
var policy = Policy
.Handle<Exception>()
.FallbackAsync(
async cancellationToken => { Console.WriteLine("fallback triggered"); })
.WrapAsync(circuitBreakerWrappingTimeout);
Console.WriteLine("Circuit state before execution: " + circuitBreaker.CircuitState);
await policy.ExecuteAsync(() => Task.Delay(-1));
Console.WriteLine("Circuit state after execution: " + circuitBreaker.CircuitState);
}
}
The code can be run with this dotnetfiddle sample: https://dotnetfiddle.net/m9O3cg
(dotnetfiddle sample changed to a non-async main only because dotnetfiddle did not always await
the async Main()
method to complete, so the output was not always complete with async Main()
)
The output is:
Circuit state before execution: Closed
fallback triggered
Circuit state after execution: Open
This demonstrates that the circuit-breaker policy is being reached/taking part in your execution.
TimeoutRejectedException
TimeoutRejectedException
s it has experienced: 1. That is enough to break, so the circuit-breaker transitions to open state.TimeoutRejectedException
(it is only a measuring-and-breaking device)TimeoutRejectedException
and outputs the fallback.Another execution through policy
within the 30-second durationOfBreak
would fail with BrokenCircuitException
, due to the circuit being open.