Using Polly Retry Policies in C#

by Daniel Wood

Using Polly Retry Policies in C#

When connecting to external APIs, you cannot guarantee their stability; network, outage, overloading issues can all interfere with your application and its logic.

One way to handle this is to use a for loop to retry an API call if it fails:

private async Task<ApiResponse> CallApiWithRetries(string endpoint)
{
    const int maxRetries = 3;
    const int delayMs = 1000;
    
    for (int attempt = 1; attempt <= maxRetries; attempt++)
    {
        try 
        {
            var response = await _httpClient.GetAsync(endpoint);
            return await response.Content.ReadFromJsonAsync<ApiResponse>();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Attempt {attempt} failed: {ex.Message}");
            
            if (attempt == maxRetries)
                throw; // Rethrow after final attempt
                
            await Task.Delay(delayMs); // Wait before retrying
        }
    }
}

This works, but it’s not great. The retry logic is mixed with the business logic, and it gets confusing and messy to customise and work with. That’s where the tool Polly comes in.

Getting Started with Polly

Polly is a .NET resilience and transient-fault-handling library that helps you implement retry, circuit breaker, timeout, and other stability patterns in a clean, maintainable way.

I will demonstrate how to setup and start using Polly in your project.

First, add the NuGet packages:

dotnet add package Polly
dotnet add package Microsoft.Extensions.Http.Polly

Basic Retry Policy

Let’s create a simple retry policy:

using Polly;

var retryPolicy = Policy
    .Handle<HttpRequestException>()
    .Or<TimeoutException>()
    .WaitAndRetryAsync(3, retryAttempt => 
        TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
try
{
    await retryPolicy.ExecuteAsync(async () => 
    {
        var response = await httpClient.GetAsync("https://api.example.com/data");
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<ApiResponse>();
    });
}
catch (Exception ex)
{
        Console.WriteLine($"All retries failed: {ex.Message}");
}

This code handles both HttpRequestException and TimeoutException with an exponential backoff (the delay doubles after each failure).

Common Use Cases for Retry Policies

Database Connections

SQL Server and other databases can have transient connection issues, especially in cloud environments:

var sqlRetryPolicy = Policy
    .Handle<SqlException>(ex => ex.Number == 1205 || ex.Number == -2) // Deadlock or timeout
    .WaitAndRetryAsync(5, retryAttempt => 
        TimeSpan.FromMilliseconds(200 * retryAttempt));

await sqlRetryPolicy.ExecuteAsync(async () => 
{
    using var connection = new SqlConnection(_connectionString);
    await connection.OpenAsync();
    // Execute commands...
});

Rate Limited APIs

Random jitterer = new Random();

var rateLimitPolicy = Policy
    .Handle<HttpRequestException>()
    .OrResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.TooManyRequests)
    .WaitAndRetryAsync(5, retryAttempt => 
    {
        var baseDelay = TimeSpan.FromSeconds(Math.Pow(2, retryAttempt));
        var jitter = TimeSpan.FromMilliseconds(jitterer.Next(0, 1000));
        return baseDelay + jitter;
    });

If lots of users retry an API call at the same time, it can overload the service. That’s where jitter comes in — it adds a bit of randomness to the delay, so retries are spread out more evenly.

This simple tweak helps avoid retry storms and makes your app more reliable under pressure.

Conclusion

Polly has transformed how I handle retries in my applications. Instead of scattering retry logic throughout my code, I now define policies once and reuse them everywhere. My code is cleaner, more maintainable, and more resilient to real-world failures.

The examples above just scratch the surface of what Polly can do. It’s highly configurable and can be tailored to almost any scenario you’ll encounter. Give it a try in your next project – you’ll wonder how you ever managed without it!

Related Posts

Leave a Comment