Skip to content
Peter Csajtai edited this page Jul 9, 2020 · 4 revisions

When the number of failures exceeds a given threshold, this bot prevents the continuous re-execution of the failing operation by blocking the traffic for a configured amount of time. This usually could give some break to the remote resource to heal itself properly.

Default implementation

Implements the Circuit Breaker pattern described here. It can be used by configuring the Botpolicy like this:

  • For operations without a return value
    // Create a new bot policy
    var policy = new BotPolicy();
    
    // Configure the policy to use the default circuit breaker
    policy.Configure(policyConfig => policyConfig
        .CircuitBreaker(circuitBreakerConfig => circuitBreakerConfig
    
            // Sets the amount of time of how long the circuit breaker should 
            // remain in the Open state before turning into HalfOpen.
            .DurationOfOpen(TimeSpan.FromSeconds(15))
    
            // Sets the delegate to determine whether the 
            // given operation should be marked as failed when a specific exception occurs.
            .BrakeWhenExceptionOccurs(exception => exception is HttpRequestException),
    
                // Configure the default circuit breaker strategy
                strategyConfig => strategyConfig
    
                    // Sets the maximum number of failed operations before 
                    // the circuit breaker turns into the Open state.
                    .FailureThresholdBeforeOpen(5)
    
                    // Sets the minimum number of succeeded operations should 
                    // be reached when the circuit breaker is in HalfOpen state 
                    // before turning into Closed.
                    .SuccessThresholdInHalfOpen(2)));
  • For operations with a return value
    // Create a new bot policy
    var policy = new BotPolicy<HttpResponseMessage>();
    
    // Configure the policy to use the default circuit breaker
    policy.Configure(policyConfig => policyConfig
        .CircuitBreaker(circuitBreakerConfig => circuitBreakerConfig
    
            // Sets the amount of time of how long the circuit breaker should 
            // remain in the Open state before turning into HalfOpen.
            .DurationOfOpen(TimeSpan.FromSeconds(15))
    
            // Sets the delegate to determine whether the 
            // given operation should be marked as failed based on its return value.
            .BrakeWhenResultIs(result => !result.IsSucceeded),
    
                // Configure the default circuit breaker strategy
                strategyConfig => strategyConfig
    
                    // Sets the maximum number of failed operations before 
                    // the circuit breaker turns into the Open state.
                    .FailureThresholdBeforeOpen(5)
    
                    // Sets the minimum number of succeeded operations should 
                    // be reached when the circuit breaker is in HalfOpen state 
                    // before turning into Closed.
                    .SuccessThresholdInHalfOpen(2)));

Custom implementation

The functionality of the Circuit Breaker bot can be extended with a custom CircuitBreakerStrategy implementation. It can give more control over the internal behavior by determining when should the Circuit switch between states.

class CustomCircuitBreakerStrategy : CircuitBreakerStrategy
{
   public DefaultCircuitBreakerStrategy(CircuitBreakerConfigurationBase config)
       :base(config)
   {}

   // Called when the underlying operation is failed within the Closed circuit state.
   // Returns true if the circuit should open, otherwise false.
   protected override bool OperationFailedInClosed()
   {
       // You can count here how many executions failed in the Closed state.
   }

   // Called when the underlying operation is failed within the HalfOpen circuit state.
   // Should return true if the circuit should move back to open 
   // state from half open, otherwise false.
   protected override bool OperationFailedInHalfOpen()
   {
       // You can decide what should happen when an operation fails in
       // the HalfOpen state, remain in HalfOpen or open the circuit again.
   }

   // Called when the underlying operation is succeeded within the HalfOpen circuit state.
   // Returns true if the circuit should be closed, otherwise false.
   protected override bool OperationSucceededInHalfOpen()
   {
       // You can decide what should happen when an operation succeeds in
       // the HalfOpen state, close the circuit or remain in half open for a while.
   }

   protected override void Reset()
   {
       // Called when the underlying operation is succeeded within the Closed circuit state.
   }
}

Then you can use your custom strategy by configuring the Botpolicy like this:

policy.Configure(policyConfig => policyConfig
    .CustomCircuitBreaker(circuitBreakerConfig => 

        // Construct your custom implementation
        new CustomCircuitBreakerStrategy(circuitBreakerConfig),

            // Configure the circuit breaker bot
            circuitBreakerConfig => circuitBreakerConfig

            // Sets the amount of time of how long the circuit breaker should 
            // remain in the Open state before turning into HalfOpen.
            .DurationOfOpen(TimeSpan.FromSeconds(15))

            // Sets the delegate to determine whether the 
            // given operation should be marked as failed based on its return value.
            .BrakeWhenResultIs(result => !result.IsSucceeded)));

Available configuration options

  • Same for policies with or without a return value

    • .BrakeWhenExceptionOccurs(Func<Exception, bool>) - Sets the delegate to determine whether the given operation should be marked as failed by the CircuitBreakerStrategy or not when a specific exception occurs.
    • .DurationOfOpen(TimeSpan openStateDuration) - Sets the amount of time of how long the circuit breaker should remain in the Open state before turning into HalfOpen.
    • .WithStateHandler(ICircuitStateHandler stateHandler) - Sets a the underlying circuit state handler implementation.
    • .OnOpen(Action<TimeSpan> openHandler) - Sets the delegate which will be invoked when the circuit breaker trips to the open state.
    • .OnClosed(Action closedHandler) - Sets the delegate which will be invoked when the circuit breaker trips to the closed state.
    • .OnHalfOpen(Action halfOpenHandler) - Sets the delegate which will be invoked when the circuit breaker trips to the half open state.
  • Only for policies with a return value

    • .BrakeWhenResultIs(Func<TResult, bool>) - Sets the delegate to determine whether the given operation should be marked as failed by the CircuitBreakerStrategy or not based on its return value.

When an operation execution attempt is made when the Circuit Breaker is in Open state, it throws a CircuitOpenException exception.

When there is more than one operation executing during the HalfOpen state, the second operation would be interrupted with a HalfOpenExecutionLimitExceededException exception.

Circuit state handler

The circuit state handler is an other abstraction which allows the customization of the Circuit Breakers internal behavior. Usually it could be used to store the actual Circuit State, however there are other use cases where it could be very useful when you have the ability to modify the handling of that state. As an example you can check the DistributedCircuitStateHandler implementation used to simulate the case when the Circuit State is shared between several Circuit Breakers in a distributed cache.

If you'd like to create a custom state handler you have to implement the ICircuitStateHandler interface:

public class DistributedCircuitStateHandler : ICircuitStateHandler
    {
        public CircuitState Read()
        {
            // Reads the current circuit state.
        }

        public void Update(CircuitState state)
        {
            // Updates the actual state, it's called when the CircuitBreakerStrategy
            // chooses to switch to another state.
        }

        public Task<CircuitState> ReadAsync(CancellationToken token, bool continueOnCapturedContext)
        {
            // Reads the current circuit state asynchronously.            
        }

        public Task UpdateAsync(CircuitState state, CancellationToken token, bool continueOnCapturedContext)
        {
             // Updates the actual state asynchronously.
        }
    }

Then you can set your custom implementation like this:

policy.Configure(policyConfig => policyConfig
    .CircuitBreaker(circuitBreakerConfig => circuitBreakerConfig
        .WithStateHandler(new DistributedCircuitStateHandler()), /* strategy config */));