How to use Polly In C#
By FoxLearn 3/13/2025 2:27:10 AM 168
The Polly .NET library simplifies retry logic by abstracting away the details.
First, you create a retry policy and apply it to your error-prone code:
// Build the policy var retryPolicy = Policy.Handle<TransientException>() .WaitAndRetry(retryCount: 3, sleepDurationProvider: _ => TimeSpan.FromSeconds(1)); // Execute the error-prone code with the policy var attempt = 0; retryPolicy.Execute(() => { Log($"Attempt {++attempt}"); throw new TransientException(); });
In this example, if a TransientException
is thrown, Polly will retry the operation after waiting for 1 second, retrying up to 3 times.
The output would look like:
03:22:26.56244 Attempt 1 03:22:27.58430 Attempt 2 03:22:28.58729 Attempt 3 03:22:29.59790 Attempt 4 Unhandled exception. TransientException: Exception of type 'TransientException' was thrown.
Notice the final output after the 3 retries: Polly stops retrying and lets the exception bubble up.
In this article, we’ll explore more on how to use Polly for retries and provide a comprehensive example involving HttpClient requests.
To install Polly, run the following command in the Package Manager Console:
Install-Package Polly
Once installed, include the necessary using statement:
using Polly;
Executing Logic During Retries with onRetry
The onRetry
parameter allows you to execute logic during retries, such as logging or performing additional actions. Here's an example where we log the retry attempt and duration:
using Polly; var MAX_RETRIES = 3; // Build the policy var retryPolicy = Policy.Handle<TransientException>() .WaitAndRetry(retryCount: MAX_RETRIES, sleepDurationProvider: (attemptCount) => TimeSpan.FromSeconds(attemptCount * 2), onRetry: (exception, sleepDuration, attemptNumber, context) => { Log($"Transient error. Retrying in {sleepDuration}. Attempt {attemptNumber} / {MAX_RETRIES}"); }); // Execute the error-prone code retryPolicy.Execute(() => { throw new TransientException(); });
This outputs:
04:11:18.25781 Transient error. Retrying in 00:00:02. 1 / 3 04:11:20.28769 Transient error. Retrying in 00:00:04. 2 / 3 04:11:24.29990 Transient error. Retrying in 00:00:06. 3 / 3 Unhandled exception. TransientException: Exception of type 'TransientException' was thrown.
Configuring Retry Delays
You can control the delay before each retry using the sleepDurationProvider
parameter. Here are different ways to calculate retry delays:
Fixed Delay:
_ => TimeSpan.FromSeconds(1)
Variable Delay Based on Attempt Count:
(attemptCount) => TimeSpan.FromSeconds(attemptCount * 2)
Exponential Backoff with Jitter (for many concurrent requests): More complex strategies like exponential backoff with jitter can spread retry attempts over time, reducing pressure on the server.
Retry Without Delay
For certain scenarios, retrying without a delay makes more sense. For example, if you’re running a prediction algorithm prone to transient errors, you may want to adjust parameters between retries instead of waiting. Here’s how you can retry without delay and adjust parameters:
using Polly; int attempt = 0; int speed = 15; int airIntake = 15; // Build the policy var retryPolicy = Policy.Handle<TransientException>() .Retry(retryCount: 3, onRetry: (exception, attemptNumber) => { // Adjust parameters to reduce transient errors speed -= 5; airIntake -= 5; }); // Execute the error-prone code retryPolicy.Execute(() => { Log($"Attempt #{++attempt} - CalculationPredictions(speed: {speed}, airIntake: {airIntake})"); CalculatePredictions(speed, airIntake); Log("Completed calculations"); });
The output would be:
Attempt #1 - CalculationPredictions(speed: 15, airIntake: 15) Attempt #2 - CalculationPredictions(speed: 10, airIntake: 10) Attempt #3 - CalculationPredictions(speed: 5, airIntake: 5) Completed calculations
Retrying HttpClient Requests with Polly
When making HTTP requests, encountering transient errors is almost inevitable. Let’s set up retry logic for handling the "Too Many Requests" (HTTP 429) error code, using Polly for retries.
WeatherClient - Retrying HttpClient Requests with Polly
using Polly; using Polly.Retry; public class WeatherClient { private readonly HttpClient httpClient; private AsyncRetryPolicy retryPolicy; public WeatherClient(IRetryDelayCalculator retryDelayCalculator) { httpClient = new HttpClient(); int MAX_RETRIES = 3; retryPolicy = Policy.Handle<HttpRequestException>(ex => ex.StatusCode == HttpStatusCode.TooManyRequests) .WaitAndRetryAsync( retryCount: MAX_RETRIES, sleepDurationProvider: retryDelayCalculator.Calculate, onRetry: (exception, sleepDuration, attemptNumber, context) => { Log($"Too many requests. Retrying in {sleepDuration}. {attemptNumber} / {MAX_RETRIES}"); }); } private void Log(string message) { Console.WriteLine($"{DateTime.Now:hh:mm:ss.ffff} {message}"); } public async Task<string> GetWeather() { return await retryPolicy.ExecuteAsync(async () => { var response = await httpClient.GetAsync("https://localhost:12345/weatherforecast"); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(); }); } }
WeatherService - Simulating Errors
[ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; [HttpGet] public IActionResult Get() { var rng = new Random(); if (rng.Next() % 3 == 0) return StatusCode((int)HttpStatusCode.TooManyRequests); return Ok(Summaries[rng.Next(Summaries.Length)]); } }
Exponential Backoff with Jitter
For handling concurrent requests, use exponential backoff with jitter to avoid overwhelming the server.
public interface IRetryDelayCalculator { TimeSpan Calculate(int attemptNumber); } public class ExponentialBackoffWithJitterCalculator : IRetryDelayCalculator { private readonly Random random = new Random(); public TimeSpan Calculate(int attemptNumber) { int jitter = random.Next(10, 200); return TimeSpan.FromSeconds(Math.Pow(2, attemptNumber - 1)) + TimeSpan.FromMilliseconds(jitter); } }
When running this retry logic, you'll observe how it retries based on the backoff strategy, potentially succeeding or failing depending on the server's responses.
By applying retry logic with Polly, you improve resilience and reduce the impact of transient errors in your applications.
- How to batch read with Threading.ChannelReader in C#
- How to ignore JSON deserialization errors in C#
- JsonException: A possible object cycle was detected
- JSON value could not be converted to System.String in C#
- Calling ‘BuildServiceProvider’ from application code results in an additional copy of singleton services being created
- How to use Newtonsoft in ASP.NET Core
- Global exception event handlers in C#
- How to Add or overwrite a value in ConcurrentDictionary in C#