Skip to content

Polly

Developer UtilitiesUtilitiesC#

What it is

Polly is a .NET resilience library providing retry, circuit breaker, timeout, bulkhead isolation, rate limiting and fallback as composable policies.

Build a resilience pipeline from strategies. Order matters: strategies added first are outermost, so a timeout added before retry bounds the whole operation.

Installation

dotnet add package Polly

Getting started

The smallest useful thing you can do with it, and what each part means.

Retry with backoff and a circuit breaker
var pipeline = new ResiliencePipelineBuilder<HttpResponseMessage>()
    .AddRetry(new RetryStrategyOptions<HttpResponseMessage>
    {
        MaxRetryAttempts = 3,
        Delay = TimeSpan.FromMilliseconds(200),
        BackoffType = DelayBackoffType.Exponential,
        UseJitter = true,          // avoids synchronised retry storms
        ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
            .HandleResult(r => (int)r.StatusCode >= 500)
            .Handle<HttpRequestException>(),
    })
    .AddCircuitBreaker(new CircuitBreakerStrategyOptions<HttpResponseMessage>
    {
        FailureRatio = 0.5,
        SamplingDuration = TimeSpan.FromSeconds(30),
        BreakDuration = TimeSpan.FromSeconds(15),
    })
    .AddTimeout(TimeSpan.FromSeconds(10))
    .Build();

var response = await pipeline.ExecuteAsync(
    async ct => await client.GetAsync(url, ct), cancellationToken);
Jitter matters more than it sounds: without it, every client that failed at the same moment retries at the same moment, and the recovering service is knocked over again.

Advanced usage

Where the library earns its place over a simpler alternative.

Registering with HttpClientFactory
builder.Services.AddHttpClient<BookApiClient>(c =>
{
    c.BaseAddress = new Uri("https://api.example.com");
})
.AddStandardResilienceHandler(options =>
{
    options.Retry.MaxRetryAttempts = 3;
    options.CircuitBreaker.FailureRatio = 0.5;
    options.AttemptTimeout.Timeout = TimeSpan.FromSeconds(5);
    options.TotalRequestTimeout.Timeout = TimeSpan.FromSeconds(20);
});
AddStandardResilienceHandler applies a sensible pre-built pipeline. Note the two timeouts: one bounds a single attempt, the other the whole operation including retries.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

BrokenCircuitException
The breaker is open because the failure ratio was exceeded. This is intended — handle it with a fallback rather than retrying through it.
Retries made an outage worse
No jitter, no budget, or retrying non-idempotent writes. Add jitter and cap total attempts.

Best practices

  • Only retry idempotent operations, or you will duplicate side effects.
  • Always enable jitter — synchronised retries turn a partial outage into a full one.
  • Pair retries with a circuit breaker so a failing dependency stops receiving traffic.
  • Set both a per-attempt and a total timeout; one alone leaves a gap.

Background

Why it exists, and what it was reacting to.

Polly encodes the resilience patterns from Michael Nygard's Release It! as reusable strategies, and is now integrated into Microsoft's own HTTP client factory.