How to use Retry Pattern using Polly in C#

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.

  1. 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.

  2. 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.

  3. 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:

Console ApplicationPollyHttpClientAPIExecute HTTP request with retry policySend HTTP requestMake HTTP requestReturn responseReturn successful responseReturn successReturn transient errorWait and retryRetry HTTP requestMake HTTP request (retry)Return response (retry)Return successful responseReturn successReturn retry errorReturn failurealt[Retry Success][Retry Error]alt[Response Success][Transient Error]Console ApplicationPollyHttpClientAPI

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:

Console ApplicationPollyHttpClientAPIExecute request with circuit breaker policySend requestMake requestReturn responseReturn successful responseReturn successReturn errorCircuit breaker open (tracking error)Throw exception (circuit breaker open)alt[Response Success][Response Error]Retry request (after circuit breaker cooldown)Send request (retry)Make request (retry)Return response (retry)Return successful responseCircuit breaker resetReturn successReturn errorCircuit breaker open (tracking error)Throw exception (circuit breaker open)alt[Retry Success][Retry Error]Console ApplicationPollyHttpClientAPI

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:

Console ApplicationPollyHttpClientAPIExecute request with timeout policySend requestMake requestReturn responseReturn successful responseReturn successReturn errorThrow TimeoutRejectedExceptionThrow exceptionalt[Timeout Error][Other Error]alt[Response Success][Response Error]Console ApplicationPollyHttpClientAPI

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.

Next Post Previous Post
No Comment
Add Comment
comment url