Failure and Fallback

You may want some sort of backstop exception handler, that allows you to take compensating action, such as undoing any partially committed work, issuing a compensating transaction, or queuing work for later delivery (perhaps using the External Bus).

To support this we provide a IHandleRequests<TRequest>Fallback method. In the Fallback method you write your code to run in the event of failure.

Calling the Fallback Pipeline

We provide a FallbackPolicy Attribute that you can use on your IHandleRequests<TRequest>.Handle() method. The implementation of the Fallback Policy Handler is straightforward: it creates a backstop exception handler by encompassing later requests in the Request Handling Pipeline in a try...catch block. You can configure it to catch all exceptions, or just Broken Circuit Exceptions when a Circuit Breaker has tripped.

When the Fallback Policy Handler catches an exception it calls the IHandleRequests<TRequest>.Fallback() method of the next Handler in the pipeline, as determined by IHandleRequests<TRequest>.Successor

The implementation of RequestHandler<T>.Fallback() uses the same Russian Doll approach as it uses for RequestHandler<T>.Handle(). This means that the request to take compensating action for failure, flows through the same pipeline as the request for service, allowing each Handler in the chain to contribute.

In addition the Fallback Policy Handler makes the originating exception available to subsequent Handlers using the Context Bag with the key: CAUSE_OF_FALLBACK_EXCEPTION

Using the FallbackPolicy Attribute

The following example shows a Handler with Request Handler Attributes for Retry and Circuit Breaker policies that is configured with a Fallback Policy which catches a Broken Circuit Exception (raised when the Circuit Breaker is tripped) and initiates the Fallback chain.

public class MyFallbackProtectedHandler: RequestHandler<MyCommand>
{
    [FallbackPolicy(backstop: false, circuitBreaker: true, step: 1)]
    [UsePolicy(new [] {}"MyCircuitBreakerStrategy", "MyRetryStrategy"}, step: 2)]
    public override MyCommand Handle(MyCommand command)
    {
        /*Do some work that can fail*/
    }

    public override MyCommand Fallback(MyCommand command)
    {
        if (Context.Bag.ContainsKey(FallbackPolicyHandler<MyCommand>.CAUSE_OF_FALLBACK_EXCEPTION))
        {
            /*Use fallback information to determine what action to take*/
        }
        return base.Fallback(command);
    }
}

Scope of a Fallback

Where you put any FallbackPolicy attribute determines what exceptions it will call your Fallback method to guard against. This is controlled by the Step parameter. Remember that you encapsulate anything with a higher Step and can react to an exception thrown there.

Last updated