Polly
Update 29th Aug 2021
I’m now using Dapper and Polly - please see this article for most up to date thinking.
Update 14th Oct 2020
I’ve shied away from using Polly in favour of a simpler implementation. MPActors Source is a good start.
My 2020 article on Dapper contains current thoughts and implementations.
Introduction
I’m writing articles on developing a website broken link checker in C#. This is part of that series.
There is a intro article on how I used Dapper
In my broken link checker I’m using SQL Azure, which very infrequently will not work, so I needed some kind of retry mechanism in my SQL calls. I reached out to the excellent Polly library.
I’m also doing a lot of Http calls in my broken link checker, so have explored how polly could help too.
SQL
https://hyr.mn/dapper-and-polly/ has a good tutorial where he uses an extension method.
public static async Task<IEnumerable<Actor>> GetTop10ActorsWithRetry(string connectionString)
=> await WithConnection(connectionString, async x =>
{
// Using an extension method to call the polly retry code
var result = await x.QueryAsyncWithRetry<Actor>(
@"SELECT TOP 10 *
FROM Actors");
return result;
});
We can make the code more terse using our wrapper function. This means we don’t need the extension methods.
public static async Task<T> WithConnection<T>(
string connectionString,
Func<IDbConnection, Task<T>> func)
{
await using var conn = new SqlConnection(connectionString);
//return await func(conn);
return await DapperExtensions.RetryPolicy.ExecuteAsync(() => func(conn));
}
Http
- RetryNTimes
- WaitAndRetryNTimes
- WaitAndRetryNTimesWithExponentialBackOff
I like this simple backoff strategy then eventual failure.
var httpClient = new HttpClient();
var url = "https://localhost:44307/api/values";
// Define our policy:
var policy = Policy.Handle<Exception>().WaitAndRetryAsync(
6, // Could do forever ie miss this out, but good to do this ie max of 6400ms then it will fail
attempt => TimeSpan.FromSeconds(0.1 * Math.Pow(2,
attempt)), // Back off! 200,400,800,1600ms etc..
(exception, calculatedWaitDuration) =>
{
Console.WriteLine($"{exception.Message} : Auto delay for {calculatedWaitDuration.TotalMilliseconds}ms");
});
while (true)
{
try
{
await policy.ExecuteAsync(async () =>
{
var httpResponseMessage = await httpClient.GetAsync(url);
// throws if .IsSuccessStatusCode property is false
httpResponseMessage.EnsureSuccessStatusCode();
var sc = httpResponseMessage.StatusCode;
Console.WriteLine($"Status code is {(int)sc}{sc}");
var content = await httpResponseMessage.Content.ReadAsStringAsync();
Console.WriteLine($"Content is {content}");
});
}
catch (Exception ex)
{
Console.WriteLine($"Request eventually failed with {ex.Message}");
}
await Task.Delay(500);
}
Conclusion
This could be much simplified without having to use Polly. So that is what I’m considering ie a simple exponential back off (using code similar to above).