Post

Building Resilient .NET Applications with Polly

Building Resilient .NET Applications with Polly

Polly is a .NET library designed to help developers handle transient failures gracefully.
With Polly, you can implement resilience patterns like retries, circuit breakers, timeouts, and more, ensuring your application remains robust in the face of failures.

Polly

Why Polly?

In distributed systems, temporary failures such as network interruptions or overloaded services are common. Polly enables you to mitigate these issues by:

  • Retrying failed requests.
  • Limiting the impact of repeated failures using circuit breakers.
  • Applying timeouts to avoid indefinite waits.
  • Providing fallback mechanisms for degraded service.

Key Resilience Patterns with Polly

Retry

Automatically retries a failed operation a specified number of times.

1
2
3
4
5
6
7
8
9
var retryPolicy = Policy
    .Handle<HttpRequestException>() // Define the exception to handle
    .WaitAndRetryAsync(
        retryCount: 3,
        sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
        onRetry: (exception, timeSpan, retryCount, context) =>
        {
            Console.WriteLine($"Retry {retryCount} will be attempt {retryCount + 1} after {timeSpan}. Error: {exception.Message}.\n");
        });
1
2
3
4
await retryPolicy.ExecuteAsync(async () =>
{
    // Action...
}

Circuit Breaker

Prevents further calls when a specified failure threshold is reached, allowing the system to recover.

1
2
3
4
5
6
7
8
var circuitBreakerPolicy = Policy
    .Handle<HttpRequestException>() // Define the exception to handle
    .CircuitBreakerAsync(
        exceptionsAllowedBeforeBreaking: 2,
        durationOfBreak: TimeSpan.FromSeconds(10),
        onBreak: (exception, timespan) => Console.WriteLine("CIRCUIT BROKEN FOR 10 SECONDS!"),
        onReset: () => Console.WriteLine("CIRCUIT RESET! \n"));

1
2
3
4
5
6
7
8
9
10
11
12
try
{
    await circuitBreakerPolicy.ExecuteAsync(async () =>
    {
        // Action...
    });
}
catch(Polly.CircuitBreaker.BrokenCircuitException)
{
    Console.WriteLine($"Circuit is open. No action launched.");
    Console.WriteLine("Waiting for circuit to close... \n");
}

Timeout

Sets a maximum time limit for an operation to complete.

1
2
3
4
var timeoutPolicy = Policy
    .TimeoutAsync(
        seconds: 5,
        timeoutStrategy: TimeoutStrategy.Pessimistic);
1
2
3
4
5
6
7
8
9
10
11
try
{
    await timeoutPolicy.ExecuteAsync(async () =>
    {
        // Action...
    });
}
catch(TimeoutRejectedException)
{
    Console.WriteLine("Timeout occurred.");
}

Fallback

Fallbacks act as a safety net when all other policies fail. They provide an alternative action, like returning a default value, logging errors, or redirecting to a backup service, ensuring the application remains functional instead of crashing.

1
2
3
4
5
6
7
8
9
10
var fallbackPolicy = Policy
    .Handle<HttpRequestException>() // Define the exception to handle
    .FallbackAsync(
        async (cancellationToken) =>
        {
            Console.WriteLine("Fallback: Action taken due to failure.\n");
            await Task.Delay(1000, cancellationToken);
            return;
        }
    );
1
2
3
4
await fallbackPolicy.ExecuteAsync(async () =>
{
    // Action...
});

Policy Wrap

Combines multiple policies to address complex scenarios.

1
2
3
4
5
var resiliencePolicy = Policy.WrapAsync(
  retryPolicy, 
  circuitBreakerPolicy, 
  timeoutPolicy, 
  fallbackPolicy);

Example: Using Polly with HttpClientFactory

Polly integrates seamlessly with HttpClientFactory in .NET Core and later. Here’s an example configuration:

1
2
3
4
5
6
7
services.AddHttpClient("ResilientClient")
    .AddPolicyHandler(Policy
        .Handle<HttpRequestException>()
        .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(retryAttempt)))
    .AddPolicyHandler(Policy
        .Handle<HttpRequestException>()
        .CircuitBreakerAsync(2, TimeSpan.FromMinutes(1)));

Benefits of Using Polly

  1. Improved Resilience: Handle failures gracefully without impacting user experience.
  2. Flexibility: Combine multiple policies to meet specific needs.
  3. Ease of Use: Fluent API for intuitive configuration.
  4. Compatibility: Works well with modern .NET tools like HttpClientFactory.

Conclusion

Polly is an indispensable library for building reliable, failure-tolerant .NET applications. By leveraging its patterns, you can ensure your application remains operational even under challenging conditions. Start integrating Polly today and make resilience a core feature of your systems.

Check out how it works. Pay special attention to when messages like “CIRCUIT BROKEN FOR 10 SECONDS!” and “CIRCUIT RESET!” are printed.
GitHub ResilienceExample

This post is licensed under CC BY 4.0 by the author.