How to use Retry Pattern using Polly in C#
Effective error handling is crucial for building robust and resilient software applications. In this blog post, we’ll explore how to handle different types of errors using retry logic and the powerful Polly library in C# console applications. We’ll cover transient HTTP errors, circuit breakers, timeouts, and provide code examples with detailed explanations.
Error Types and Handling with Polly
Polly offers various error-handling policies to handle different types of errors effectively. Let’s discuss each error type and how Polly can help us handle them.
-
Transient HTTP Errors: Transient HTTP errors are temporary failures that can occur during network communication. Retry logic is often employed to handle such errors and ensure successful request execution. Polly simplifies this process with its retry policy.
-
Circuit Breakers: Circuit breakers provide a way to handle system failures and prevent cascading failures in distributed systems. By monitoring the error rate and opening the circuit when necessary, circuit breakers help reduce the load on failing systems. Polly offers circuit breaker policies to integrate this behavior into our applications.
-
Timeouts: Timeouts are essential to prevent requests from waiting indefinitely and causing performance issues. Polly provides timeout policies to limit the time a request can take, enabling us to handle timeouts effectively.
Let’s dive into each error type and see how we can handle them using Polly in C# console applications.
Handling Transient HTTP Errors with Polly
Transient HTTP errors can occur due to various reasons, such as network connectivity issues or temporary server failures. To handle these errors, we can use Polly’s retry policy, which retries the request for a specified number of times with a delay between each retry.
Consider the following code example:
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Polly;
using Polly.Extensions.Http;
class Program
{
static async Task Main()
{
int maxRetries = 3;
int retryDelayMilliseconds = 2000;
bool success = await ExecuteHttpRequestWithRetry(maxRetries, retryDelayMilliseconds);
if (success)
{
Console.WriteLine("HTTP request succeeded.");
}
else
{
Console.WriteLine("HTTP request failed after maximum retries.");
}
}
static async Task<bool> ExecuteHttpRequestWithRetry(int maxRetries, int retryDelayMilliseconds)
{
var httpClient = new HttpClient()
{
BaseAddress = new Uri("https://jsonplaceholder.typicode.com")
};
var retryPolicy = HttpPolicyExtensions
.HandleTransientHttpError()
.WaitAndRetryAsync(maxRetries, i => TimeSpan.FromMilliseconds(retryDelayMilliseconds));
HttpResponseMessage response = await retryPolicy.ExecuteAsync(() =>
httpClient.GetAsync("todos/1"));
if (response.IsSuccessStatusCode)
{
// Process the successful response here
return true; // Request succeeded
}
else
{
Console.WriteLine($"HTTP request failed with status code: {response.StatusCode}");
return false; // Request failed
}
}
}
In the above example, we define a retry policy using Polly’s HandleTransientHttpError
and WaitAndRetryAsync
methods. The HandleTransientHttpError
method instructs Polly to consider specific HTTP status codes (e.g., 500, 502, etc.) as transient errors. The WaitAndRetryAsync
method specifies the maximum number of retries (maxRetries
) and the delay between each retry (retryDelayMilliseconds
).
The ExecuteAsync
method is used to execute the HTTP request within the retry policy. If the request succeeds (returns a success status code), we process the response and return true
. Otherwise, we log the failure and return false
.
The sequence diagram below illustrates the flow of actions involved in handling transient HTTP errors with Polly:
Handling Circuit Breakers with Polly
Circuit breakers are an important mechanism to prevent cascading failures and provide resilience in distributed systems. Polly simplifies the implementation of circuit breakers by providing circuit breaker policies.
Consider the following code example:
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Polly;
class Program
{
static async Task HandleCircuitBreaker()
{
var httpClient = new HttpClient()
{
BaseAddress = new Uri("https://jsonplaceholder.typicode.com")
};
var circuitBreakerPolicy = Policy
.Handle<Exception>()
.CircuitBreakerAsync(3, TimeSpan.FromSeconds(10));
try
{
await circuitBreakerPolicy.ExecuteAsync(async () =>
{
HttpResponseMessage response = await httpClient.GetAsync("posts");
if (response.IsSuccessStatusCode)
{
Console.WriteLine("Circuit breaker handled successful request.");
}
else
{
throw new Exception("Error occurred.");
}
});
}
catch (Exception ex)
{
Console.WriteLine($"Circuit breaker handling failed. Exception: {ex.Message}");
}
}
}
In the above example, we define a circuit breaker policy using Polly’s Handle
and CircuitBreakerAsync
methods. The Handle
method specifies the type of exception to be considered as a failure. The CircuitBreakerAsync
method sets the threshold values for opening and closing the circuit breaker (e.g., 3
failures within 10
seconds).
The ExecuteAsync
method is used to execute the HTTP request within the circuit breaker policy. If the request succeeds (returns a success status code), we process the response. If the request fails, we throw an exception to simulate an error.
The sequence diagram below illustrates the flow of actions involved in handling circuit breakers with Polly:
Handling Timeouts with Polly
Timeouts are essential to prevent requests from waiting indefinitely and causing performance issues. Polly provides timeout policies to limit the time a request can take, enabling us to handle timeouts effectively.
Consider the following code example:
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Polly;
class Program
{
static async Task HandleTimeout()
{
var httpClient = new HttpClient()
{
BaseAddress = new Uri("https://jsonplaceholder.typicode.com")
};
var timeoutPolicy = Policy.TimeoutAsync(TimeSpan.FromSeconds(5));
try
{
await timeoutPolicy.ExecuteAsync(async () =>
{
HttpResponseMessage response = await httpClient.GetAsync("posts");
if (response.IsSuccessStatusCode)
{
Console.WriteLine("Timeout handled successful request.");
}
else
{
throw new Exception("Error occurred.");
}
});
}
catch (TimeoutRejectedException)
{
Console.WriteLine("Timeout handling failed. Request took too long to respond.");
}
catch (Exception ex)
{
Console.WriteLine($"Timeout handling failed. Exception: {ex.Message}");
}
}
}
In the above example, we define a timeout policy using Polly’s TimeoutAsync
method. The TimeoutAsync
method specifies the maximum duration for the request to complete (TimeSpan.FromSeconds(5)
in this case).
The ExecuteAsync
method is used to execute the HTTP request within the timeout policy. If the request succeeds (returns a success status code), we process the response. If the request fails, we throw an exception to simulate an error.
The sequence diagram below illustrates the flow of actions involved in handling timeouts with Polly:
I hope this blog post provides a clearer understanding of how to handle different types of errors with retry logic using Polly in C# console applications.