2

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));
Peter Csala
  • 17,736
  • 16
  • 35
  • 75
  • Hi! Are you able to post the complete code you are using when you are expecting the circuitBreaker to 'work' / 'be reached'? (Is that to say: you have code which you expect to trigger a break of the circuit? Or?). The code posted so far has a single call `await policy.ExecuteAsync(() => Task.Delay(-1));`. The circuit will only break if `_settings.ConsecutiveExceptionsAllowedBeforeBreaking` consecutive calls which fail are placed through the circuitBreaker policy. So from the code posted so far, the circuit will only break if `_settings.ConsecutiveExceptionsAllowedBeforeBreaking == 1`. – mountain traveller Dec 22 '17 at 15:33
  • Also possibly relevant: The `circuitBreaker` policy you have defined here will not absorb `TimeoutRejectedException` (in case that is what you were expecting). It will rethrow it, after counting the occurrence. A circuit-breaker is only a measuring-and-(if-fault-threshold-exceeded)-breaking device, but it rethrows all exceptions. See: https://github.com/App-vNext/Polly/wiki/Circuit-Breaker#exception-handling and https://stackoverflow.com/a/37101237/. Hope this helps. If not, can you define what behav you are expecting but not seeing? - what you mean by 'reach' the breaker, or it 'work'ing? – mountain traveller Dec 22 '17 at 15:45
  • 1
    And: here is a [dotnetfiddle sample](https://dotnetfiddle.net/Oc9HNL) showing CircuitBreaker working. And the [Polly Samples](https://github.com/App-vNext/Polly-Samples) numbers 6, 7 and onwards have examples which can be run from Visual Studio or similar. – mountain traveller Dec 22 '17 at 16:17

1 Answers1

4

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.

  • The executed delegate is timed out by the timeout policy, which throws TimeoutRejectedException
  • The circuit-breaker policy catches that and counts the number of consecutive TimeoutRejectedExceptions it has experienced: 1. That is enough to break, so the circuit-breaker transitions to open state.
  • The circuit-breaker rethrows the TimeoutRejectedException (it is only a measuring-and-breaking device)
  • The fallback policy catches the TimeoutRejectedException and outputs the fallback.
  • The final line outputting circuit state demonstrates the circuit-breaker was affected by (so did take part in) the call.

Another execution through policy within the 30-second durationOfBreak would fail with BrokenCircuitException, due to the circuit being open.

Azat
  • 2,275
  • 1
  • 28
  • 32
mountain traveller
  • 7,591
  • 33
  • 38
  • Thank you very much! Somehow I missed that I was only calling once... But your further explanation also helped me a lot with making the actual Policy I ended up with. Perfect! – Louise Ahokas Jan 03 '18 at 09:11