I just came across the Polly library whilst listening to Carl Franklin’s “Better know a framework” on .NET Rocks and it looks to be a perfect fit for use on a project I’m working on which makes calls to various services and thus can encounter various types of exceptions, some which might succeed if retried after a certain amount of time (for example).
To install Polly via NuGet simple use
Install-Package Polly
What is it?
Check out Polly on GitHub
So basically Polly allows you to create a Policy which can then be used to execute against a method which might have exceptions – so for example maybe the method calls a webservice and as well as possibly getting exceptions from the webservice may have exceptions on the client such as the service being down or the likes.
A policy basically defines which exceptions to handle, what to do when an exception occurs and you can tell Polly to retry the original method or break and stop the method being called again until a certain timespan has passed.
A policy is created using a fluent style interface, so let’s take a look at some simple examples
Note: Polly on GitHub has many examples of using the code which are more far better and complete than I intend to show here.
var policy = Policy
.Handle<ArgumentOutOfRangeException>()
.Retry();
try
{
policy.Execute(() => service.Calculate(a, b));
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
In the above example we create a policy object using the PolicyBuilder (fluent) syntax. We need to end the method calls with Retry, RetryForever, CirtcuitBreaker or WaitAndRetry (or the Async variants) to get a Policy object created. We are using an empty Retry means Retry the method invoked via the Execute method once. We can also specify a number of retries if we want. So if the call to service.Calculate fails with an ArgumentOutOfRangeException Polly will execute the method once more and if it still fails we will get the exception propagated back into our application code, hence we still need to look to handle exceptions in our own try..catch block or ofcourse via our application’s unhandled exception mechanism.
In the code sample, we have only listed a single exception that the policy attempts to retry on, but we can list multiple exceptions that we want to retry on and/or we can supply functionality to the handler to decide what to do when an exception occurs, which obviously makes the whole exception handling/retry mechanism more configurable.
Handle and Or can do a little more
So the Handle and therefore the Or methods can also do a little more than just handle the exception, they also allow us to supply a function which takes the exception and returns a boolean. From this we can be more selective of the exceptions we handle, for example
var policy = Policy
.Handle<ArgumentOutOfRangeException>(ex =>
ex.ParamName == "a")
.Retry();
In the code above, we’re simply saying, if the exception param name is “a” then retry executing the method otherwise the exception is propagated without any retries etc.
Handling multiple exceptions
To handle multiple exceptions we write the following
var policy = Policy
.Handle<ArgumentOutOfRangeException>()
.Or<DivideByZeroException>()
.Or<SomeOtherException>()
.Retry();
In the above we list the three exception types we want to retry the execution method on receiving.
Execute the method
The Policy Execute method is what ultimately calls the code which we’re wrapping in the policy. So the following is calling our service’s Calculate method and it’s within this block that any retries etc. occur.
policy.Execute(() => service.Calculate(a, b));
We can also handle calls to functions with return values using
var result = policy.Execute(() => service.Calculate(a, b));
There’s also the ability to pass some data as context through the policy (discussed in part a little later) when specifying methods to handle context we instead get a ContextualPolicy object back and this allows us to pass a dictionary of string/object key/values into the Execute method which can then be used within the policy user-defined code.
Retry and retry again
The Retry syntax in Polly allows us to do retry once, retry multiple times and more, so let’s look at some code samples of this method and see what each does
Policy.
.Handle<SoapException>
.Retry(); // retry once
Policy.
.Handle<SoapException>
.Retry(3); // retry 3 times
Policy
.Handle<SoapException>
.Retry((ex, count) =>
{
// maybe log the exception or do something else
}); // retry and call an action on each retry,
// supplying the exception and retry count
Policy
.Handle<SoapException>
.Retry(3, (ex, count) => // retry 3 times
{
// maybe log the exception or do something else
}); // retry and call an action on each retry,
// supplying the exception and retry count
//
Policy
.Handle<SoapException>
.Retry((ex, count, context) =>
{
// maybe log the exception or do something else
}); // retry and call an action on each retry,
// supplying the exception and retry count
Policy
.Handle<SoapException>
.Retry(3, (ex, count, context) => // retry 3 times
{
// maybe log the exception or do something else
}); // retry and call an action on each retry,
// supplying the exception and retry count
The last two retry methods create a ContextPolicy which allows us to pass context information via the Execute method. So if you want to pass some for of context information in a dictionary of string, object key/values. You can do so via this mechanism.
RetryForever – does what it says
The RetryForever method does exactly what it says and will simply keep retrying executing the method, there are currently three overloads of this method
Policy
.Handle<SoapException>
.RetryForever(); // just keeping retrying
Policy
.Handle<SoapException>
.RetryForever(ex =>
{
// possibly log the exception ?
}); // just keeping retrying but allows
// us to do something on each retry
Policy
.Handle<SoapException>
.RetryForever((ex, context) =>
{
// possibly log the exception ?
}); // just keeping retrying but allows
// us to do something on each retry
// with context
WaitAndRetry, pause and then try it again
The WaitAndRetry method allows us to not only retry but also to build in a wait period, so for example when calling something like a webservice we might make a service call and if a specific exception occurs, maybe specifying the service is unavailable, we might allow the method to be executed again (retried) after a timeout period. Better still we can supply multiple timespans to the WaitAndRetry method, so for example the first time we might retry after 5 seconds, but if the service is still not available, then we might reduce the timeout (or increase it) then decrease or increase again before we stop retrying.
Policy
.Handle<SoapException>
.WaitAndRetry(3, count =>
{
return TimeSpan.FromSeconds(5);
}); // retry 3 times with a
// wait of 5 seconds each time
Policy
.Handle<SoapException>
.WaitAndRetry(3, count =>
{
return TimeSpan.FromSeconds(5);
}, (ex, ts) =>
{
// action called on each retry
}); // retry 3 times with a
// wait of 5 seconds each time
Policy
.Handle<SoapException>
.WaitAndRetry(3, count =>
{
return TimeSpan.FromSeconds(5);
}, (ex, ts, context) =>
{
// action called on each retry
// with context
}); // retry 3 times with a
// wait of 5 seconds each time
Policy
.Handle<SoapException>
.WaitAndRetry(3, new[]
{
TimeSpan.FromSeconds(10),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(1)
}); // retry 3 times using
// supplied timespans
Policy
.Handle<SoapException>
.WaitAndRetry(3, new[]
{
TimeSpan.FromSeconds(10),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(1)
}, (ex, ts) =>
{
// action called on each retry
}); // retry 3 times using
// supplied timespans
Policy
.Handle<SoapException>
.WaitAndRetry(3, new[]
{
TimeSpan.FromSeconds(10),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(1)
}, (ex, ts, context) =>
{
// action called on each retry
// with context
}); // retry 3 times using
// supplied timespans
CircuitBreaker, stop calls whilst it’s broken
The CircuitBreaker method allows us to mark a method call as broken and ensure we do not call it again. Ofcourse we probably will want to call the method again at some point and thus we can supply the number of exceptions to allow before the circuit breaker kicks in and a TimeSpan signifying the duration of the break, i.e. before it’s auto resets and we can execute the method again.
Policy
.Handle<SoapException>
.CircuitBreaker(3, TimeSpan.FromSeconds(10));
So in the above code we don’t automatically retry or anything like that. The policy will maintain state so that if we call the executed method and it exceptions, then the exception will propagate through to the caller (as it normally would with such an exception), however if when then execute the method again two more times and they both fail, then the circuit is “opened” and no further calls will be accepted, meaning we’ll get get BrokenCircuitExceptions until the duration of the break resets the circuit to closed.