Polly is a .NET 3.5 / 4.0 / 4.5 / PCL (Profile 259) library that allows developers to express transient exception handling policies such as Retry, Retry Forever, Wait and Retry or Circuit Breaker in a fluent manner.
Install-Package Polly
You can install the Strongly Named version via:
Install-Package Polly-Signed
// 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 => x.ParamName == "example")
// 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
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
});
// 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[]
{
1.Seconds(),
2.Seconds(),
3.Seconds()
}, (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[]
{
1.Seconds(),
2.Seconds(),
3.Seconds()
}, (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 (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
}
);
// Break the circuit after the specified number of exceptions
// and keep circuit broken for the specified duration
Policy
.Handle<DivideByZeroException>()
.CircuitBreaker(2, TimeSpan.FromMinutes(1));
For more information on the Circuit Breaker pattern see:
// 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());
Using the ExecuteAndCapture
method 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
You can use Polly with asynchronous functions by using the asynchronous methods
RetryAsync
RetryForeverAsync
WaitAndRetryAsync
CircuitBreakerAsync
ExecuteAsync
ExecuteAndCaptureAsync
In place of their synchronous counterparts
Retry
RetryForever
WaitAndRetry
CircuitBreaker
Execute
ExecuteAndCapture
For example
await Policy
.Handle<SqlException>(ex => ex.Number == 1205)
.Or<ArgumentException>(ex => ex.ParamName == "example")
.RetryAsync()
.ExecuteAsync(() => DoSomethingAsync());
Note: By default, continuation and retry will not run on captured synchronization context. To change this behavior use .ExecuteAsync(...)
overloads taking a boolean continueOnCapturedContext
parameter.
Async policy execution supports cancellation using .ExecuteAsync(...)
overloads taking a CancellationToken
. Cancellation cancels Policy actions such as further retries or waits between retries. The delegate taken by the relevant .ExecuteAsync(...)
overloads also takes a cancellation token input parameter, to pass to calls within the delegate supporting cancellation. For example:
// Try several times to retrieve from a uri, but support cancellation at any time.
CancellationToken cancellationToken = // ...
var policy = Policy.Handle<Exception>().WaitAndRetryAsync(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10) });
var response = await policy.ExecuteAsync(ct => httpClient.GetAsync(uri, ct), cancellationToken);
- Fluent Assertions - A set of .NET extension methods that allow you to more naturally specify the expected outcome of a TDD or BDD-style test | Apache License 2.0 (Apache)
- xUnit.net - Free, open source, community-focused unit testing tool for the .NET Framework | Apache License 2.0 (Apache)
- [Ian Griffith's TimedLock] (http://www.interact-sw.co.uk/iangblog/2004/04/26/yetmoretimedlocking)
- [Steven van Deursen's ReadOnlyDictionary] (http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=29)
- 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
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.
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!
Licensed under the terms of the New BSD License