Applications commonly depend on external databases, services, and APIs to operate in today's distributed systems and microservices architecture. High traffic, network problems, or temporary difficulties can cause certain dependencies to fail. It is our responsibility as developers to build apps that can gracefully handle such failures. Resilience patterns can help with that. We will use the most recent Polly v8 coupled with Microsoft to construct middleware for Retry and Circuit Breaker in this blog. extensions. tenacity.

When should I use a circuit breaker? What is its pattern?
Modern applications employ the Circuit Breaker design pattern to avoid invoking a service or function that is prone to malfunction. Consider it analogous to your home's electric circuit breaker, which trips and cuts off the electrical supply in the event of an overload to guard against harm. In software, a circuit breaker momentarily halts calls to a malfunctioning service in order to prevent further overloading it.

When Should You Use It?

  • When an external service is known to fail frequently.
  • When you want to fail fast instead of waiting for timeouts.
  • To prevent cascading failures in microservices.
  • To improve overall system responsiveness and stability.

Circuit Breaker Flow

What is Polly?
Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback. Polly has been widely adopted in the .NET community, and with Polly v8, Microsoft has introduced deep integration into the Microsoft.Extensions ecosystem through the new Microsoft.Extensions.Resilience package.

Polly with Microsoft.Extensions.Resilience
In Polly v8, resilience is more declarative and integrated into .NET’s dependency injection and configuration model. This makes it easier to build policies that are environment-aware and configurable. Let’s start building our retry and circuit breaker middleware using the latest packages.

Step 1. Set Up Your Project

Create a new web API project in Visual Studio, or if you are using Visual Studio Code, you can execute the following commands.
mkdir PollyResilienceApp
cd PollyResilienceApp
dotnet new PollyV8RetryDemo


Post that adds below the NuGet package
# In case of Visual Studio Code
dotnet add package Microsoft.Extensions.Resilience

# In case of Visual Studio Package Manager Console
NuGet\Install-Package Microsoft.Extensions.Resilience

Step 2. Configure Resilience Policies in the Program.cs
Open the Program.cs file and add the required namespace and Poly service to place it in the middleware. Below is the snippet of Program.cs file with service.
// namespaces
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Resilience;

var builder = WebApplication.CreateBuilder(args);

// add service
builder.Services.AddResiliencePipeline("standard-pipeline", pipelineBuilder =>
{
    // add retry configurations
    pipelineBuilder.AddRetry(new RetryStrategyOptions
    {
        MaxRetryAttempts = 3,  // -> this should be from appSettings.json
        Delay = TimeSpan.FromMilliseconds(500), // -> this should be from appSettings.json
        BackoffType = DelayBackoffType.Exponential
    });

    // set the circuit breaker pattern
    // configuration numbers should be driven from appSettings.json
    pipelineBuilder.AddCircuitBreaker(new CircuitBreakerStrategyOptions
    {
        FailureRatio = 0.5,  // -> this should be from appSettings.json
        SamplingDuration = TimeSpan.FromSeconds(30),
        MinimumThroughput = 10,
        BreakDuration = TimeSpan.FromSeconds(15)
    });
});

var app = builder.Build();

app.MapControllers();

app.Run();


Step 3. Inject and Use the Policy in a Controller
Once you set the service in the program.cs, it is time to inject the same in the Controllers. Below is an example of a sample controller where ResiliencePipelineProvider has been injected to execute
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Resilience;

[ApiController]
[Route("api/[controller]")]
public class MyAPIController : ControllerBase
{
    private readonly ResiliencePipelineProvider<string> _pipelineProvider;

    public MyAPIController(ResiliencePipelineProvider<string> pipelineProvider)
    {
        _pipelineProvider = pipelineProvider;
    }

    [HttpGet("execute")]
    public async Task<IActionResult> Execute_v1()
    {
        var pipeline = _pipelineProvider.GetPipeline("standard-pipeline");

        try
        {
            var result = await pipeline.ExecuteAsync(async token =>
            {
                // Actual operations goes here
                // For test, can place an sleep command
                // await Task.Delay(100);
                throw new HttpRequestException("Failure");
            });

            return Ok(result);
        }
        catch (Exception ex)
        {
            return StatusCode(500, $"Operation failed: {ex.Message}");
        }
    }
}

How Polly Helps Achieve the Circuit Breaker Pattern?
Polly allows you to plug in the circuit breaker logic without cluttering your business code. The resilience pipeline acts as a middleware that surrounds your code, monitoring failures, retries, and triggering breaks when needed.

With Polly, you get:

  • Observability: You can plug in logging and metrics easily.
  • Composability: You can combine Retry + Circuit Breaker + Timeout.
  • Declarative configuration: Adjust thresholds without touching the logic.

The diagram below explains how the request is flowing.

While all the retry actions are executing by Polly, logs can be done through the Logging Service. To enable the same, add the following code snippet in Program.cs. Appenders can be used based on the project needs.
builder.Services.AddLogging();

Conclusion
By integrating Polly v8 with Microsoft.Extensions.Resilience, you can build reliable, maintainable, and production-ready applications in .NET. Using the Retry and Circuit Breaker patterns, you shield your applications from transient errors and cascading failures.

If you’re building services at scale or working in a microservices environment, implementing these patterns is not just a good-to-have but a necessity.