This post is the third and final installment on the retry pattern following on from implementing a simple retry pattern in c# and the retry pattern for async tasks.
In the last two posts we created a retry helper class to allow us to add retry logic to applications without cluttering important application logic with retry code. Now we will solve the same problem using a popular OSS library.
Polly is an open source framework for that “allows developers to express transient exception and fault handling policies such as Retry, Retry Forever, Wait and Retry, or Circuit Breaker in a fluent manner”. Polly is more sophisticated than the retry helper from before and allows us to easily describe fault handling logic by creating a policy to represent the behaviour and then apply the policy to a delegate.
Background
Polly has been around for a while after originally being created by Michael Wolfenden and is now on version four, with version five just around the corner. It is currently supported by Carl Franklin’s development team App V-Next and has recently been adopted by the .Net Foundation so it is a fairly safe bet that it is well supported and will be so for a while.
Retry with Polly
So lets solve the same problem as before, having to make an async HTTP request that may occasionally fail, but this time using the retry policy in Polly.
var httpClient = new HttpClient();
var maxRetryAttempts = 3;
var pauseBetweenFailures = TimeSpan.FromSeconds(2);
var retryPolicy = Policy
.Handle<HttpRequestException>()
.WaitAndRetryAsync(maxRetryAttempts, i => pauseBetweenFailures);
await retryPolicy.ExecuteAsync(async () =>
{
var response = await httpClient
.DeleteAsync("https://example.com/api/products/1");
response.EnsureSuccessStatusCode();
});
This code exhibits the same behaviour as before, sending the HTTP DELETE, and on a failure retrying three times with two seconds between attempts. As you see the API structure in Polly is similar to the retry helper by awaiting on the ExecuteAsync method to which you pass an async lambda containing the delegate to be retried. One nice feature of Polly is that the Policy can be declared with a very descriptive fluent syntax, and each policy can be reused, even on multiple threads.
It is also easy to implement exponential back-off - where the delays between attempts increase with the number of failures. Doing so is configured when creating the Policy:
Policy
.Handle<HttpRequestException>()
.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(4),
TimeSpan.FromSeconds(8)
});
Another nice feature is that you can even calculate the delay at run-time, using the attempt count if you wish:
Policy
.Handle<HttpRequestException>()
.WaitAndRetryAsync(3, retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))
);
One Gotcha I found - if you use the Policy.ExecuteAsync
asynchronous execution API, you must use the WaitAndRetryAsync
policy creation method and not the WaitAndRetry
variant. Vice versa, if you use the non async Execute
version you have to use the corresponding non async WaitAndRetry
fluent policy builder. At first glance it looks like those could be combined but I expect there is a good reason to keep them separate.
This really just touches on the basics of Polly. A more advanced Circuit Breaker pattern implementation is also available for more sophisticated transient error handling scenarios.
To Polly or not to Polly?
Polly is capable of describing complicated fault handling behaviours in a clear and concise syntax. If my error handling requirements needed features such as exponential back-off or a circuit breaker I would have no qualms in recommending Polly, similarly if i had a need for multiple policies. If I just wanted to add simple retries in a couple of places I think the retry helper from the previous post would suffice and saves on a dependency.
(Thanks to the Polly project for their excellent library and use of the logo.)