Retry Policies with Polly
Retry policies automatically re-execute failed operations, giving transient failures a chance to resolve.
Installing Polly
bash1dotnet add package Polly
Basic Retry
csharp1using Polly;23// Retry 3 times on HttpRequestException4var policy = Policy5 .Handle<HttpRequestException>()6 .RetryAsync(3);78var result = await policy.ExecuteAsync(async () =>9{10 return await httpClient.GetStringAsync(url);11});
Retry with Callback
Monitor what's happening:
csharp1var policy = Policy2 .Handle<HttpRequestException>()3 .RetryAsync(3, onRetry: (exception, retryCount) =>4 {5 Console.WriteLine($"Retry {retryCount} due to: {exception.Message}");6 });
Wait and Retry
Add delay between retries:
csharp1// Fixed delay2var policy = Policy3 .Handle<HttpRequestException>()4 .WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(1));56// Variable delays7var policy = Policy8 .Handle<HttpRequestException>()9 .WaitAndRetryAsync(new[]10 {11 TimeSpan.FromSeconds(1),12 TimeSpan.FromSeconds(2),13 TimeSpan.FromSeconds(4)14 });1516// Calculated delay17var policy = Policy18 .Handle<HttpRequestException>()19 .WaitAndRetryAsync(3, retryAttempt =>20 TimeSpan.FromSeconds(retryAttempt)); // 1s, 2s, 3s
Handling Multiple Exception Types
csharp1var policy = Policy2 .Handle<HttpRequestException>()3 .Or<TimeoutException>()4 .Or<SocketException>()5 .WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(1));
Handling Specific Results
Retry based on response, not just exceptions:
csharp1var policy = Policy2 .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)3 .WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(1));45var response = await policy.ExecuteAsync(() => httpClient.GetAsync(url));
Retry Forever
For critical operations:
csharp1// With delay to prevent CPU spinning2var policy = Policy3 .Handle<HttpRequestException>()4 .WaitAndRetryForeverAsync(_ => TimeSpan.FromSeconds(5));
Warning: Use with circuit breaker to prevent infinite retries to a dead service.
Context for Logging
Pass context through retries:
csharp1var policy = Policy2 .Handle<HttpRequestException>()3 .WaitAndRetryAsync(4 3,5 _ => TimeSpan.FromSeconds(1),6 onRetry: (exception, timeSpan, retryCount, context) =>7 {8 var url = context["url"];9 Console.WriteLine($"Retry {retryCount} for {url}: {exception.Message}");10 });1112var result = await policy.ExecuteAsync(13 ctx => httpClient.GetStringAsync(ctx["url"]?.ToString()),14 new Context { ["url"] = "https://api.example.com" });
Async vs Sync Policies
csharp1// Async policy (for async operations)2var asyncPolicy = Policy3 .Handle<Exception>()4 .RetryAsync(3);56await asyncPolicy.ExecuteAsync(async () => await DoWorkAsync());78// Sync policy (for sync operations)9var syncPolicy = Policy10 .Handle<Exception>()11 .Retry(3);1213syncPolicy.Execute(() => DoWork());
Returning Results
csharp1var policy = Policy<string>2 .Handle<HttpRequestException>()3 .WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(1));45string result = await policy.ExecuteAsync(async () =>6{7 return await httpClient.GetStringAsync(url);8});
Complete Example
csharp1class ResilientHttpClient2{3 private readonly HttpClient _client;4 private readonly IAsyncPolicy<HttpResponseMessage> _policy;56 public ResilientHttpClient()7 {8 _client = new HttpClient();910 _policy = Policy<HttpResponseMessage>11 .Handle<HttpRequestException>()12 .OrResult(r => r.StatusCode == HttpStatusCode.ServiceUnavailable)13 .OrResult(r => r.StatusCode == HttpStatusCode.TooManyRequests)14 .WaitAndRetryAsync(15 3,16 (retryAttempt, response, context) =>17 {18 // Check for Retry-After header19 if (response.Result?.Headers.RetryAfter?.Delta != null)20 {21 return response.Result.Headers.RetryAfter.Delta.Value;22 }23 return TimeSpan.FromSeconds(Math.Pow(2, retryAttempt));24 },25 (response, timeSpan, retryCount, context) =>26 {27 Console.WriteLine($"Retry {retryCount} after {timeSpan.TotalSeconds}s");28 return Task.CompletedTask;29 });30 }3132 public async Task<string> GetAsync(string url)33 {34 var response = await _policy.ExecuteAsync(() => _client.GetAsync(url));35 response.EnsureSuccessStatusCode();36 return await response.Content.ReadAsStringAsync();37 }38}
Key Takeaways
RetryAsync(n)retries n times immediatelyWaitAndRetryAsyncadds delays between retries- Handle multiple exception types with
Or<T>() - Use
HandleResultfor response-based retry - Pass context for logging and debugging
- Use async policies for async operations
- Consider exponential backoff (next lesson)