Skip to content
forked from App-vNext/Polly

Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner. Polly targets .NET 4.0, .NET 4.5 and .NET Standard 1.0.

License

Notifications You must be signed in to change notification settings

Ashrafnet/Polly

 
 

Repository files navigation

Polly

Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.

Polly targets .NET 4.0, .NET 4.5 and .NET Standard 1.0 (coverage: .NET Core, Mono, Xamarin.iOS, Xamarin.Android, UWP, WP8.0+).

We are now a member of the .NET Foundation!

Keep up to date with new feature announcements, tips & tricks, and other news through www.thepollyproject.org

NuGet version Build status Slack Status

Installing via NuGet

Install-Package Polly

You can install the Strongly Named version via:

Install-Package Polly-Signed

.NET4.0 support is provide via the packages:

Install-Package Polly.Net40Async
Install-Package Polly.Net40Async-Signed

Resilience policies

Polly offers multiple resilience policies:

Policy Premise Aka How does the policy mitigate?
Retry
(policy family)
(quickstart ; deep)
Many faults are transient and may self-correct after a short delay. "Maybe it's just a blip" Allows configuring automatic retries.
Circuit-breaker
(policy family)
(quickstart ; deep)
When a system is seriously struggling, failing fast is better than making users/callers wait.

Protecting a faulting system from overload can help it recover.
"Stop doing it if it hurts"

"Give that system a break"
Breaks the circuit (blocks executions) for a period, when faults exceed some pre-configured threshold.
Timeout
(quickstart ; deep)
Beyond a certain wait, a success result is unlikely. "Don't wait forever" Guarantees the caller won't have to wait beyond the timeout.
Bulkhead Isolation
(quickstart ; deep)
When a process faults, multiple failing calls backing up can easily swamp resource (eg threads/CPU) in a host.

A faulting downstream system can also cause 'backed-up' failing calls upstream.

Both risk a faulting process bringing down a wider system.
"One fault shouldn't sink the whole ship" Constrains the governed actions to a fixed-size resource pool, isolating their potential to affect others.
Cache
(quickstart ; deep)
Some proportion of requests may be similar. "You've asked that one before" Provides a response from cache if known.

Stores responses automatically in cache, when first retrieved.
Fallback
(quickstart ; deep)
Things will still fail - plan what you will do when that happens. "Degrade gracefully" Defines an alternative value to be returned (or action to be executed) on failure.
PolicyWrap
(quickstart ; deep)
Different faults require different strategies; resilience means using a combination. "Defence in depth" Allows any of the above policies to be combined flexibly.

Usage – fault-handling policies

Step 1 : Specify the exceptions/faults you want the policy to handle

(for fault-handling policies: Retry family, CircuitBreaker family and Fallback)

// Single exception type
Policy
  .Handle<DivideByZeroException>()

// Single exception type with condition
Policy
  .Handle<SqlException>(ex => ex.Number == 1205)

// Multiple exception types
Policy
  .Handle<DivideByZeroException>()
  .Or<ArgumentException>()

// Multiple exception types with condition
Policy
  .Handle<SqlException>(ex => ex.Number == 1205)
  .Or<ArgumentException>(ex => ex.ParamName == "example")

Step 1b: (optionally) Specify return results you want to handle

From Polly v4.3.0 onwards, policies wrapping calls returning a TResult can also handle TResult return values:

// Handle return value with condition 
Policy
  .HandleResult<HttpResponse>(r => r.StatusCode == 404)

// Handle multiple return values 
Policy
  .HandleResult<HttpResponse>(r => r.StatusCode == 500)
  .OrResult<HttpResponse>(r => r.StatusCode == 502)

// Handle primitive return values (implied use of .Equals())
Policy
  .HandleResult<HttpStatusCode>(HttpStatusCode.InternalServerError)
  .OrResult<HttpStatusCode>(HttpStatusCode.BadGateway)
 
// Handle both exceptions and return values in one policy
int[] httpStatusCodesWorthRetrying = {408, 500, 502, 503, 504}; 
HttpResponse result = Policy
  .Handle<HttpException>()
  .OrResult<HttpResponse>(r => httpStatusCodesWorthRetrying.Contains(r.StatusCode))

For more information, see Handling Return Values at foot of this readme.

Step 2 : Specify how the policy should handle those faults

Retry

// Retry once
Policy
  .Handle<DivideByZeroException>()
  .Retry()

// Retry multiple times
Policy
  .Handle<DivideByZeroException>()
  .Retry(3)

// Retry multiple times, calling an action on each retry 
// with the current exception and retry count
Policy
    .Handle<DivideByZeroException>()
    .Retry(3, (exception, retryCount) =>
    {
        // do something 
    });

// Retry multiple times, calling an action on each retry 
// with the current exception, retry count and context 
// provided to Execute()
Policy
    .Handle<DivideByZeroException>()
    .Retry(3, (exception, retryCount, context) =>
    {
        // do something 
    });

Retry forever (until succeeds)

// Retry forever
Policy
  .Handle<DivideByZeroException>()
  .RetryForever()

// Retry forever, calling an action on each retry with the 
// current exception
Policy
  .Handle<DivideByZeroException>()
  .RetryForever(exception =>
  {
        // do something       
  });

// Retry forever, calling an action on each retry with the
// current exception and context provided to Execute()
Policy
  .Handle<DivideByZeroException>()
  .RetryForever((exception, context) =>
  {
        // do something       
  });

Wait and retry

// Retry, waiting a specified duration between each retry
Policy
  .Handle<DivideByZeroException>()
  .WaitAndRetry(new[]
  {
    TimeSpan.FromSeconds(1),
    TimeSpan.FromSeconds(2),
    TimeSpan.FromSeconds(3)
  });

// Retry, waiting a specified duration between each retry, 
// calling an action on each retry with the current exception
// and duration
Policy
  .Handle<DivideByZeroException>()
  .WaitAndRetry(new[]
  {
    TimeSpan.FromSeconds(1),
    TimeSpan.FromSeconds(2),
    TimeSpan.FromSeconds(3)
  }, (exception, timeSpan) => {
    // do something    
  }); 

// Retry, waiting a specified duration between each retry, 
// calling an action on each retry with the current exception, 
// duration and context provided to Execute()
Policy
  .Handle<DivideByZeroException>()
  .WaitAndRetry(new[]
  {
    TimeSpan.FromSeconds(1),
    TimeSpan.FromSeconds(2),
    TimeSpan.FromSeconds(3)
  }, (exception, timeSpan, context) => {
    // do something    
  });

// Retry, waiting a specified duration between each retry, 
// calling an action on each retry with the current exception, 
// duration, retry count, and context provided to Execute()
Policy
  .Handle<DivideByZeroException>()
  .WaitAndRetry(new[]
  {
    TimeSpan.FromSeconds(1),
    TimeSpan.FromSeconds(2),
    TimeSpan.FromSeconds(3)
  }, (exception, timeSpan, retryCount, context) => {
    // do something    
  });

// Retry a specified number of times, using a function to 
// calculate the duration to wait between retries based on 
// the current retry attempt (allows for exponential backoff)
// In this case will wait for
//  2 ^ 1 = 2 seconds then
//  2 ^ 2 = 4 seconds then
//  2 ^ 3 = 8 seconds then
//  2 ^ 4 = 16 seconds then
//  2 ^ 5 = 32 seconds
Policy
  .Handle<DivideByZeroException>()
  .WaitAndRetry(5, retryAttempt => 
	TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) 
  );

// Retry a specified number of times, using a function to 
// calculate the duration to wait between retries based on 
// the current retry attempt, calling an action on each retry 
// with the current exception, duration and context provided 
// to Execute()
Policy
  .Handle<DivideByZeroException>()
  .WaitAndRetry(
    5, 
    retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), 
    (exception, timeSpan, context) => {
      // do something
    }
  );

// Retry a specified number of times, using a function to 
// calculate the duration to wait between retries based on 
// the current retry attempt, calling an action on each retry 
// with the current exception, duration, retry count, and context 
// provided to Execute()
Policy
  .Handle<DivideByZeroException>()
  .WaitAndRetry(
    5, 
    retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), 
    (exception, timeSpan, retryCount, context) => {
      // do something
    }
  );

Wait and retry forever (until succeeds)

// Wait and retry forever
Policy
  .Handle<DivideByZeroException>()
  .WaitAndRetryForever(retryAttempt => 
	TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))
    );

// Wait and retry forever, calling an action on each retry with the 
// current exception and the time to wait
Policy
  .Handle<DivideByZeroException>()
  .WaitAndRetryForever(
    retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),    
    (exception, timespan) =>
    {
        // do something       
    });

// Wait and retry forever, calling an action on each retry with the
// current exception, time to wait, and context provided to Execute()
Policy
  .Handle<DivideByZeroException>()
  .WaitAndRetryForever(
    retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),    
    (exception, timespan, context) =>
    {
        // do something       
    });

For more detail see: Retry policy documentation on wiki.

Circuit Breaker

// Break the circuit after the specified number of consecutive exceptions
// and keep circuit broken for the specified duration.
Policy
    .Handle<DivideByZeroException>()
    .CircuitBreaker(2, TimeSpan.FromMinutes(1));

// Break the circuit after the specified number of consecutive exceptions
// and keep circuit broken for the specified duration,
// calling an action on change of circuit state.
Action<Exception, TimeSpan> onBreak = (exception, timespan) => { ... };
Action onReset = () => { ... };
CircuitBreakerPolicy breaker = Policy
    .Handle<DivideByZeroException>()
    .CircuitBreaker(2, TimeSpan.FromMinutes(1), onBreak, onReset);

// Break the circuit after the specified number of consecutive exceptions
// and keep circuit broken for the specified duration,
// calling an action on change of circuit state,
// passing a context provided to Execute().
Action<Exception, TimeSpan, Context> onBreak = (exception, timespan, context) => { ... };
Action<Context> onReset = context => { ... };
CircuitBreakerPolicy breaker = Policy
    .Handle<DivideByZeroException>()
    .CircuitBreaker(2, TimeSpan.FromMinutes(1), onBreak, onReset);

// Monitor the circuit state, for example for health reporting.
CircuitState state = breaker.CircuitState;

/*
CircuitState.Closed - Normal operation. Execution of actions allowed.
CircuitState.Open - The automated controller has opened the circuit. Execution of actions blocked.
CircuitState.HalfOpen - Recovering from open state, after the automated break duration has expired. Execution of actions permitted. Success of subsequent action/s controls onward transition to Open or Closed state.
CircuitState.Isolated - Circuit held manually in an open state. Execution of actions blocked.
*/

// Manually open (and hold open) a circuit breaker - for example to manually isolate a downstream service.
breaker.Isolate(); 
// Reset the breaker to closed state, to start accepting actions again.
breaker.Reset(); 

For more detail see: Circuit-Breaker documentation on wiki.

Advanced Circuit Breaker

// Break the circuit if, within any period of duration samplingDuration, 
// the proportion of actions resulting in a handled exception exceeds failureThreshold, 
// provided also that the number of actions through the circuit in the period
// is at least minimumThroughput.

Policy
    .Handle<DivideByZeroException>()
    .AdvancedCircuitBreaker(
        failureThreshold: 0.5, // Break on >=50% actions result in handled exceptions...
        samplingDuration: TimeSpan.FromSeconds(10), // ... over any 10 second period
        minimumThroughput: 8, // ... provided at least 8 actions in the 10 second period.
        durationOfBreak: TimeSpan.FromSeconds(30) // Break for 30 seconds.
                );

// Configuration overloads taking state-change delegates are
// available as described for CircuitBreaker above.

// Circuit state monitoring and manual controls are
// available as described for CircuitBreaker above.

For more detail see: Advanced Circuit-Breaker documentation on wiki.

For more information on the Circuit Breaker pattern in general see:

Fallback

// Provide a substitute value, if an execution faults.
Policy
   .Handle<Whatever>()
   .Fallback<UserAvatar>(UserAvatar.Blank)

// Specify a func to provide a substitute value, if execution faults.
Policy
   .Handle<Whatever>()
   .Fallback<UserAvatar>(() => UserAvatar.GetRandomAvatar()) // where: public UserAvatar GetRandomAvatar() { ... }

// Specify a substitute value or func, calling an action (eg for logging) if the fallback is invoked.
Policy
   .Handle<Whatever>()
   .Fallback<UserAvatar>(UserAvatar.Blank, onFallback: (exception, context) => 
    {
        // do something
    });

For more detail see: Fallback policy documentation on wiki.

Step 3 : Execute the policy

// Execute an action
var policy = Policy
              .Handle<DivideByZeroException>()
              .Retry();

policy.Execute(() => DoSomething());

// Execute an action passing arbitrary context data
var policy = Policy
    .Handle<DivideByZeroException>()
    .Retry(3, (exception, retryCount, context) =>
    {
        var methodThatRaisedException = context["methodName"];
		Log(exception, methodThatRaisedException);
    });

policy.Execute(
	() => DoSomething(),
	new Dictionary<string, object>() {{ "methodName", "some method" }}
);

// Execute a function returning a result
var policy = Policy
              .Handle<DivideByZeroException>()
              .Retry();

var result = policy.Execute(() => DoSomething());

// Execute a function returning a result passing arbitrary context data
var policy = Policy
    .Handle<DivideByZeroException>()
    .Retry(3, (exception, retryCount, context) =>
    {
        object methodThatRaisedException = context["methodName"];
        Log(exception, methodThatRaisedException)
    });

var result = policy.Execute(
    () => DoSomething(),
    new Dictionary<string, object>() {{ "methodName", "some method" }}
);

// You can of course chain it all together
Policy
  .Handle<SqlException>(ex => ex.Number == 1205)
  .Or<ArgumentException>(ex => ex.ParamName == "example")
  .Retry()
  .Execute(() => DoSomething());

Usage – general resilience policies

Step 1 : Configure

Timeout

// Timeout and return to the caller after 30 seconds, if the executed delegate has not completed.  Optimistic timeout: Delegates should take and honour a CancellationToken.
Policy
  .Timeout(30)

// Configure timeout as timespan.
Policy
  .Timeout(TimeSpan.FromMilliseconds(2500))

// Configure variable timeout via a func provider.
Policy
  .Timeout(() => myTimeoutProvider)) // Func<TimeSpan> myTimeoutProvider

// Timeout after 30 seconds, if the executed delegate has not completed.  Enforces a timeout on delegates which have no in-built timeout and do not honour CancellationToken, at the expense (in synchronous executions) of an extra thread.
// (for more detail, see deep documentation)
Policy
  .Timeout(30, TimeoutStrategy.Pessimistic)

// Timeout, calling an action if the action times out
Policy
  .Timeout(30, onTimeout: (context, timespan, task) => 
    {
        // do something 
    });

// Eg timeout, logging that the execution timed out:
Policy
  .Timeout(30, onTimeout: (context, timespan, task) => 
    {
        logger.Warn($"{context.PolicyKey} at {context.ExecutionKey}: execution timed out after {timespan.TotalSeconds} seconds.");
    });

// Eg timeout, capturing any exception from the timed-out task when it completes:
Policy
  .Timeout(30, onTimeout: (context, timespan, task) => 
    {
        task.ContinueWith(t => {
            if (t.IsFaulted) logger.Error($"{context.PolicyKey} at {context.ExecutionKey}: execution timed out after {timespan.TotalSeconds} seconds, with: {t.Exception}.");
        });
    });

For more detail see: Timeout policy documentation on wiki.

Bulkhead

// Restrict executions through the policy to a maximum of twelve concurrent actions.
Policy
  .Bulkhead(12)

// Restrict executions through the policy to a maximum of twelve concurrent actions, 
// with up to two actions waiting for an execution slot in the bulkhead if all slots are taken.
Policy
  .Bulkhead(12, 2)

// Restrict concurrent executions, calling an action if an execution is rejected
Policy
  .Bulkhead(12, context => 
    {
        // do something 
    });

// Monitor the bulkhead available capacity, for example for health/load reporting.
var bulkhead = Policy.Bulkhead(12, 2);
// ...
int freeExecutionSlots = bulkhead.BulkheadAvailableCount;
int freeQueueSlots     = bulkhead.QueueAvailableCount;

For more detail see: Bulkhead policy documentation on wiki.

Cache

The Cache policy is targeting Polly v5.1. Check out www.thepollyproject.org for updates.

PolicyWrap

// Define a combined policy strategy, built of previously-defined policies.
var policyWrap = Policy
  .Wrap(fallback, cache, retry, breaker, timeout, bulkhead);
// (wraps the policies around any executed delegate: fallback outermost ... bulkhead innermost)
policyWrap.Execute(...)

// Define a standard resilience strategy ...
PolicyWrap commonResilience = Policy.Wrap(retry, breaker, timeout);

// ... then wrap in extra policies specific to a call site, at that call site:
Avatar avatar = Policy
   .Handle<Whatever>()
   .Fallback<Avatar>(Avatar.Blank)
   .Wrap(commonResilience)
   .Execute(() => { /* get avatar */ });

// Share commonResilience, but wrap different policies in at another call site:
Reputation reps = Policy
   .Handle<Whatever>()
   .Fallback<Reputation>(Reputation.NotAvailable)
   .Wrap(commonResilience)
   .Execute(() => { /* get reputation */ });  

For more detail see: PolicyWrap documentation on wiki.

Step 2 : Execute the policy.

As for fault-handling policies above.

Post-execution: capturing the result, or any final exception

Using the ExecuteAndCapture(...) methods you can capture the result of executing a policy.

var policyResult = Policy
              .Handle<DivideByZeroException>()
              .Retry()
              .ExecuteAndCapture(() => DoSomething());
/*              
policyResult.Outcome - whether the call succeeded or failed         
policyResult.FinalException - the final exception captured, will be null if the call succeeded
policyResult.ExceptionType - was the final exception an exception the policy was defined to handle (like DivideByZeroException above) or an unhandled one (say Exception). Will be null if the call succeeded.
policyResult.Result - if executing a func, the result if the call succeeded or the type's default value
*/

Policy Keys and Context data

// Identify policies with a PolicyKey, using the WithPolicyKey() extension method
// (for example, for correlation in logs or metrics)

var policy = Policy
    .Handle<DataAccessException>()
    .Retry(3, onRetry: (exception, retryCount, context) =>
       {
           logger.Error($"Retry {retryCount} of {context.PolicyKey} at {context.ExecutionKey}, due to: {exception}.");
       })
    .WithPolicyKey("MyDataAccessPolicy");

// Identify call sites with an ExecutionKey, by passing in a Context
var customerDetails = policy.Execute(myDelegate, new Context("GetCustomerDetails"));

// "MyDataAccessPolicy" -> context.PolicyKey 
// "GetCustomerDetails  -> context.ExecutionKey


// Pass additional custom information from call site into execution context 
var policy = Policy
    .Handle<DataAccessException>()
    .Retry(3, onRetry: (exception, retryCount, context) =>
       {
           logger.Error($"Retry {retryCount} of {context.PolicyKey} at {context.ExecutionKey}, getting {context["Type"]} of id {context["Id"]}, due to: {exception}.");
       })
    .WithPolicyKey("MyDataAccessPolicy");

int id = ... // customer id from somewhere
var customerDetails = policy.Execute(() => GetCustomer(id), 
    new Context("GetCustomerDetails", new Dictionary<string, object>() {{"Type","Customer"},{"Id",id}}
    ));

For more detail see: Keys and Context Data on wiki.

Thread safety

All Polly policies are fully thread-safe. You can safely re-use policies at multiple call sites, and execute through policies concurrently on different threads.

While the internal operation of the policy is thread-safe, this does not magically make delegates you execute through the policy thread-safe: if delegates you execute through the policy are not thread-safe, they remain not thread-safe.

Asynchronous Support

Polly fully supports asynchronous executions, using the asynchronous methods:

  • RetryAsync
  • WaitAndRetryAsync
  • CircuitBreakerAsync
  • (etc)
  • ExecuteAsync
  • ExecuteAndCaptureAsync

In place of their synchronous counterparts:

  • Retry
  • WaitAndRetry
  • CircuitBreaker
  • (etc)
  • Execute
  • ExecuteAndCapture

For example

await Policy
  .Handle<SqlException>(ex => ex.Number == 1205)
  .Or<ArgumentException>(ex => ex.ParamName == "example")
  .RetryAsync()
  .ExecuteAsync(() => DoSomethingAsync());

SynchronizationContext

Async continuations and retries by default do not run on a captured synchronization context. To change this, use .ExecuteAsync(...) overloads taking a boolean continueOnCapturedContext parameter.

Cancellation support

Async policy execution supports cancellation via .ExecuteAsync(...) overloads taking a CancellationToken.

The token you pass as the cancellationToken parameter to the ExecuteAsync(...) call serves two purposes:

  • It cancels Policy actions such as further retries, waits between retries or waits for a bulkhead execution slot.
  • It is passed by the policy as the CancellationToken input parameter to any delegate executed through the policy, to support cancellation during delegate execution.
// Try several times to retrieve from a uri, but support cancellation at any time.
CancellationToken cancellationToken = // ...
var policy = Policy
    .Handle<WebException>()
    .Or<HttpRequestException>()
    .WaitAndRetryAsync(new[] { 
        TimeSpan.FromSeconds(1), 
        TimeSpan.FromSeconds(2), 
        TimeSpan.FromSeconds(4) 
    });
var response = await policy.ExecuteAsync(ct => httpClient.GetAsync(uri, ct), cancellationToken);

From Polly v5.0, synchronous executions also support cancellation via CancellationToken.

.NET4.0 support

The .NET4.0 package uses Microsoft.Bcl.Async to add async support. To minimise dependencies on the main Polly nuget package, the .NET4.0 version is available as separate Nuget packages Polly.Net40Async and Polly.Net40Async-signed.

Handing return values, and Policy<TResult>

As described at step 1b, from Polly v4.3.0 onwards, policies can handle return values and exceptions in combination:

// Handle both exceptions and return values in one policy
int[] httpStatusCodesWorthRetrying = {408, 500, 502, 503, 504}; 
HttpResponse result = Policy
  .Handle<HttpException>()
  .OrResult<HttpResponse>(r => httpStatusCodesWorthRetrying.Contains(r.StatusCode))
  .Retry(...)
  .Execute( /* some Func<HttpResponse> */ )

The exceptions and return results to handle can be expressed fluently in any order.

Strongly-typed Policy<TResult>

Configuring a policy with .HandleResult<TResult>(...) or .OrResult<TResult>(...) generates a strongly-typed Policy<TResult> of the specific policy type, eg Retry<TResult>, AdvancedCircuitBreaker<TResult>.

These policies must be used to execute delegates returning TResult, ie:

  • Execute(Func<TResult>) (and related overloads)
  • ExecuteAsync(Func<CancellationToken, Task<TResult>>) (and related overloads)

ExecuteAndCapture<TResult>()

.ExecuteAndCapture(...) on non-generic policies returns a PolicyResult with properties:

policyResult.Outcome - whether the call succeeded or failed         
policyResult.FinalException - the final exception captured; will be null if the call succeeded
policyResult.ExceptionType - was the final exception an exception the policy was defined to handle (like DivideByZeroException above) or an unhandled one (say Exception)? Will be null if the call succeeded.
policyResult.Result - if executing a func, the result if the call succeeded; otherwise, the type's default value

.ExecuteAndCapture<TResult>(Func<TResult>) on strongly-typed policies adds two properties:

policyResult.FaultType - was the final fault handled an exception or a result handled by the policy? Will be null if the delegate execution succeeded. 
policyResult.FinalHandledResult - the final fault result handled; will be null or the type's default value, if the call succeeded

State-change delegates on Policy<TResult> policies

In non-generic policies handling only exceptions, state-change delegates such as onRetry and onBreak take an Exception parameter.

In generic-policies handling TResult return values, state-change delegates are identical except they take a DelegateResult<TResult> parameter in place of Exception. DelegateResult<TResult> has two properties:

  • Exception // The exception just thrown if policy is in process of handling an exception (otherwise null)
  • Result // The TResult just raised, if policy is in process of handling a result (otherwise default(TResult))

BrokenCircuitException<TResult>

Non-generic CircuitBreaker policies throw a BrokenCircuitException when the circuit is broken. This BrokenCircuitException contains the last exception (the one which caused the circuit to break) as the InnerException.

For CircuitBreakerPolicy<TResult> policies:

  • A circuit broken due to an exception throws a BrokenCircuitException with InnerException set to the exception which triggered the break (as previously).
  • A circuit broken due to handling a result throws a BrokenCircuitException<TResult> with the Result property set to the result which caused the circuit to break.

3rd Party Libraries

Acknowledgements

  • lokad-shared-libraries - Helper assemblies for .NET 3.5 and Silverlight 2.0 that are being developed as part of the Open Source effort by Lokad.com (discontinued) | New BSD License
  • @michael-wolfenden - The creator and mastermind of Polly!
  • @ghuntley - Portable Class Library implementation.
  • @mauricedb - Async implementation.
  • @robgibbens - Added existing async files to PCL project
  • Hacko - Added extra NotOnCapturedContext call to prevent potential deadlocks when blocking on asynchronous calls
  • @ThomasMentzel - Added ability to capture the results of executing a policy via ExecuteAndCapture
  • @yevhen - Added full control of whether to continue on captured synchronization context or not
  • @reisenberger - Added full async cancellation support
  • @reisenberger - Added async support for ContextualPolicy
  • @reisenberger - Added ContextualPolicy support for circuit-breaker
  • @reisenberger - Extended circuit-breaker for public monitoring and control
  • @reisenberger - Added ExecuteAndCapture support with arbitrary context data
  • @kristianhald and @reisenberger - Added AdvancedCircuitBreaker
  • @reisenberger - Allowed async onRetry delegates to async retry policies
  • @Lumirris - Add new Polly.Net40Async project/package supporting async for .NET40 via Microsoft.Bcl.Async
  • @SteveCote - Added overloads to WaitAndRetry and WaitAndRetryAsync methods that accept an onRetry delegate which includes the attempt count.
  • @reisenberger - Allowed policies to handle returned results; added strongly-typed policies Policy<TResult>;.
  • @christopherbahr - Added optimisation for circuit-breaker hot path.
  • @Finity - Fixed circuit-breaker threshold bug.
  • @reisenberger - Add some missing ExecuteAndCapture/Async overloads.
  • @brunolauze - Add CancellationToken support to synchronous executions (to support TimeoutPolicy).
  • @reisenberger - Add PolicyWrap.
  • @reisenberger - Add Fallback policy.
  • @reisenberger - Add PolicyKeys and context to all policy executions, as bedrock for policy events and metrics tracking executions.
  • @reisenberger, and contributions from @brunolauze - Add Bulkhead Isolation policy.
  • @reisenberger - Add Timeout policy.
  • @reisenberger - Fix .NETStandard 1.0 targeting. Remove PCL259 target. PCL259 support is provided via .NETStandard1.0 target, going forward.

Sample Projects

Polly-Samples contains practical examples for using various implementations of Polly. Please feel free to contribute to the Polly-Samples repository in order to assist others who are either learning Polly for the first time, or are seeking advanced examples and novel approaches provided by our generous community.

Instructions for Contributing

Please check out our Wiki for contributing guidelines. We are following the excellent GitHub Flow process, and would like to make sure you have all of the information needed to be a world-class contributor!

Since Polly is part of the .NET Foundation, we ask our contributors to abide by their Code of Conduct.

Also, we've stood up a Slack channel for easier real-time discussion of ideas and the general direction of Polly as a whole. Be sure to join the conversation today!

License

Licensed under the terms of the New BSD License

Laptop Stickers!

Show your support for Polly by getting your laptop sticker today, available from Sticker Mule!

Polly Sticker

About

Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner. Polly targets .NET 4.0, .NET 4.5 and .NET Standard 1.0.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • C# 99.8%
  • Other 0.2%