Fallback

Dylan Reisenberger edited this page Jul 6, 2018 · 8 revisions
Clone this wiki locally

Fallback Policy (v5.0 onwards)

Purpose

To provide a substitute value (or substitute action to be actioned) in the event of failure.

Premise: 'If all else fails, degrade gracefully'

Outright failures will still occur: plan for what you will do when that happens.

Syntax

TResult-returning calls

Return a specific fallback result:

Policy<UserAvatar>
   .Handle<Whatever>()
   .Fallback<UserAvatar>(UserAvatar.Blank)

Run a function to provide a fallback result:

Policy<UserAvatar>
   .Handle<Whatever>()
   .Fallback<UserAvatar>(() => UserAvatar.GetRandomAvatar()) 

With the second example, the fallback is of course not evaluated unless it is actually needed.

void-returning calls

A FallbackPolicy may also be used on void-returning calls. In this case, it specifies an alternate Action to be run if the policy handles a fault (rather than substitute return value).

Policy
   .Handle<Whatever>()
   .Fallback(() => DoFallbackAction()) 

Syntax examples above are sync; comparable async overloads exist for asynchronous operation. See readme and wiki for more details.

Operation

  • executes the supplied delegate
  • if the delegate throws a handled exception or returns a handled result:
    • calls any configured onFallback/Async delegate
    • then calls the configured fallback Func/Action

Interacting with policy operation

OnFallback

An optional onFallback / onFallbackAsync delegate allows specific code to be executed (for example for logging) before the fallback Func/Action is invoked.

// Specify a substitute value or func, calling an action (eg for logging) if the fallback is invoked.
Policy<UserAvatar>
   .Handle<Whatever>()
   .Fallback<UserAvatar>(UserAvatar.Blank, onFallback: (result, context) => 
    {
        logger.Error($"{context.PolicyKey} at {context.ExecutionKey}: fallback value substituted, due to: {result.Exception}.");
    });

FallbackPolicy with ExecuteAndCapture()

Note that .ExecuteAndCapture/Async(...) captures whether the overall execution result is one that would have been considered a failure by the policy. Therefore, if your FallbackPolicy replaces a failure result with one that would be considered a success (as many fallback policies do - the "graceful degrade" pattern), then .ExecuteAndCapture/Async(...) will naturally report PolicyResult.Outcome == OutcomeType.Success.

Thread safety and policy reuse

Thread safety

FallbackPolicy is thread-safe: multiple calls may safely be placed concurrently through a policy instance.

Policy reuse

FallbackPolicy instances may be re-used across multiple call sites.

When reusing policies, use an ExecutionKey to distinguish different call-site usages within logging and metrics.