NEW Browse AI tools across categories — updated daily. See what's new →

Resilience

Polly v8 resilience patterns for .NET — retry, circuit breaker, timeout, hedging, and IHttpClientFactory integration using Microsoft.Extensions.Resilience.

Version1.0.0
LicenseMIT
Token count~2,325
UpdatedJun 5, 2026

Install

Quick install

via npx skills · works with 57+ agents
npx skills add https://github.com/zdanovichnick/dotnet-pilot
Or pick agent:
npx skills add zdanovichnick/dotnet-pilot --agent claude-code
npx skills add zdanovichnick/dotnet-pilot --agent cursor
npx skills add zdanovichnick/dotnet-pilot --agent codex
npx skills add zdanovichnick/dotnet-pilot --agent opencode
npx skills add zdanovichnick/dotnet-pilot --agent github-copilot
npx skills add zdanovichnick/dotnet-pilot --agent windsurf
More install options

Shorthand — useful for multi-skill repos:

npx skills add zdanovichnick/dotnet-pilot

Manual — clone the repo and drop the folder into your agent's skills directory:

git clone https://github.com/zdanovichnick/dotnet-pilot.git
cp -r dotnet-pilot ~/.claude/skills/
How to use: Once installed, ask your agent to "use the Resilience skill" or describe what you want (e.g. "Polly v8 resilience patterns for .NET — retry, circuit breaker, timeout, hedging"). Requires Node.js 18+.

Resilience

Polly v8 resilience patterns for .NET — retry, circuit breaker, timeout, hedging, and IHttpClientFactory integration using Microsoft.Extensions.Resilience.

---
name: resilience
description: Polly v8 resilience patterns for .NET — retry, circuit breaker, timeout, hedging, and IHttpClientFactory integration using Microsoft.Extensions.Resilience.
---

Resilience Patterns (Polly v8)

Reference for building fault-tolerant .NET services using Polly v8 and Microsoft.Extensions.Resilience. Used by dnp-planner and dnp-tdd-developer-hard.

Package Reference

<PackageReference Include="Microsoft.Extensions.Resilience" Version="9.*" />
<!-- For HTTP clients: -->
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="9.*" />

Breaking change from Polly v7: the Policy.Handle<>().Retry() API is gone. Use ResiliencePipelineBuilder exclusively.

Core Concepts

| Concept | Purpose |
|---------|---------|
| ResiliencePipeline | Executes a delegate through a chain of strategies |
| ResiliencePipelineBuilder | Fluent builder — add strategies in order (outermost first) |
| ResiliencePipelineProvider<TKey> | DI-resolved registry; resolve pipelines by key |
| ResilienceContext | Per-execution metadata (cancellation token, properties) |

Strategies execute in the order they are added. Outer strategies wrap inner ones — add timeout last to apply it per-attempt, or first to apply it to the whole pipeline.

DI Registration

builder.Services.AddResiliencePipeline("database", pipeline =>
    pipeline
        .AddRetry(new RetryStrategyOptions
        {
            MaxRetryAttempts = 3,
            Delay = TimeSpan.FromSeconds(1),
            BackoffType = DelayBackoffType.Exponential,
            UseJitter = true,
            ShouldHandle = new PredicateBuilder()
                .Handle<SqlException>()
                .Handle<TimeoutException>(),
            OnRetry = args =>
            {
                // args.Context, args.AttemptNumber, args.Outcome available
                Console.WriteLine($"Retry {args.AttemptNumber} after {args.RetryDelay}");
                return ValueTask.CompletedTask;
            }
        })
        .AddCircuitBreaker(new CircuitBreakerStrategyOptions
        {
            FailureRatio = 0.5,
            SamplingDuration = TimeSpan.FromSeconds(30),
            MinimumThroughput = 5,
            BreakDuration = TimeSpan.FromSeconds(15)
        })
        .AddTimeout(TimeSpan.FromSeconds(5)));

Retry Strategy

.AddRetry(new RetryStrategyOptions
{
    MaxRetryAttempts = 3,

    // Fixed, Linear, or Exponential
    BackoffType = DelayBackoffType.Exponential,
    Delay = TimeSpan.FromSeconds(1),  // base delay; doubles each attempt with Exponential

    // Adds ±20% random jitter to prevent thundering herd
    UseJitter = true,

    // Only retry on specific exceptions or results
    ShouldHandle = new PredicateBuilder()
        .Handle<HttpRequestException>()
        .HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.TooManyRequests),

    OnRetry = args =>
    {
        logger.LogWarning(
            "Retry attempt {Attempt} after {Delay}ms due to {Exception}",
            args.AttemptNumber,
            args.RetryDelay.TotalMilliseconds,
            args.Outcome.Exception?.Message);
        return ValueTask.CompletedTask;
    }
})

Circuit Breaker

Opens the circuit when the failure ratio exceeds the threshold, stopping all calls for BreakDuration.

.AddCircuitBreaker(new CircuitBreakerStrategyOptions
{
    // Open circuit if ≥50% of calls fail
    FailureRatio = 0.5,

    // Measurement window
    SamplingDuration = TimeSpan.FromSeconds(30),

    // Minimum calls in window before circuit can open
    MinimumThroughput = 5,

    // How long circuit stays open before moving to half-open
    BreakDuration = TimeSpan.FromSeconds(15),

    OnOpened = args =>
    {
        logger.LogError("Circuit opened for {Duration}s", args.BreakDuration.TotalSeconds);
        return ValueTask.CompletedTask;
    },
    OnClosed = _ =>
    {
        logger.LogInformation("Circuit closed — service recovered");
        return ValueTask.CompletedTask;
    }
})

When the circuit is open, calls throw BrokenCircuitException immediately without hitting the dependency.

Timeout

// Per-attempt timeout (placed after retry — each attempt gets its own timeout)
.AddTimeout(TimeSpan.FromSeconds(5))

// Or with options for callbacks
.AddTimeout(new TimeoutStrategyOptions
{
    Timeout = TimeSpan.FromSeconds(5),
    OnTimeout = args =>
    {
        logger.LogWarning("Operation timed out after {Timeout}", args.Timeout);
        return ValueTask.CompletedTask;
    }
})

Hedging (Parallel Fallback Requests)

Sends a duplicate request after a delay if the first hasn't returned. Uses the first successful response.

.AddHedging(new HedgingStrategyOptions<HttpResponseMessage>
{
    // Send a second request after 500ms if first hasn't returned
    Delay = TimeSpan.FromMilliseconds(500),

    // How many parallel hedged requests to allow
    MaxHedgedAttempts = 2,

    ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
        .HandleResult(r => !r.IsSuccessStatusCode),

    ActionGenerator = args => () =>
        ValueTask.FromResult(Outcome.FromResult(
            args.PrimaryContext.Properties.GetValue(
                new ResiliencePropertyKey<HttpResponseMessage>("hedged-result"),
                null!)))
})

Use hedging for latency-sensitive read operations where idempotency is guaranteed.

IHttpClientFactory Integration

Standard Resilience Handler (recommended shortcut)

Applies retry + circuit breaker + timeout + rate limiter in one call:

builder.Services.AddHttpClient("payments", client =>
    {
        client.BaseAddress = new Uri(builder.Configuration["PaymentsApi:BaseUrl"]!);
    })
    .AddStandardResilienceHandler(options =>
    {
        options.Retry.MaxRetryAttempts = 3;
        options.CircuitBreaker.FailureRatio = 0.5;
        options.TotalRequestTimeout.Timeout = TimeSpan.FromSeconds(30);
    });

Custom Pipeline per Client

builder.Services.AddHttpClient("inventory")
    .AddResilienceHandler("inventory-pipeline", pipeline =>
    {
        pipeline
            .AddRetry(new RetryStrategyOptions<HttpResponseMessage>
            {
                MaxRetryAttempts = 2,
                ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
                    .Handle<HttpRequestException>()
                    .HandleResult(r => r.StatusCode >= HttpStatusCode.InternalServerError)
            })
            .AddTimeout(TimeSpan.FromSeconds(10));
    });

Consuming the Named Client

public class InventoryClient(IHttpClientFactory factory)
{
    public async Task<InventoryResponse?> GetStockAsync(string sku, CancellationToken ct)
    {
        var client = factory.CreateClient("inventory");
        var response = await client.GetAsync($"/stock/{sku}", ct);
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<InventoryResponse>(ct);
    }
}

Named Pipeline Usage (Non-HTTP)

public class OrderRepository(
    AppDbContext db,
    ResiliencePipelineProvider<string> pipelines,
    ILogger<OrderRepository> logger)
{
    public async Task<Order?> GetByIdAsync(int id, CancellationToken ct)
    {
        var pipeline = pipelines.GetPipeline("database");

        return await pipeline.ExecuteAsync(
            async token => await db.Orders.FindAsync([id], token),
            ct);
    }
}

Recommended Pipeline Compositions

| Use Case | Strategies (outer → inner) |
|----------|---------------------------|
| Database queries | Retry (3x exponential) → Timeout (5s) |
| HTTP API calls | Retry (3x) → Circuit Breaker → Timeout (10s total) |
| Payment processing | Circuit Breaker → Timeout (30s) — no retry (idempotency risk) |
| Read-heavy low-latency | Hedging → Timeout |
| Background job step | Retry (5x linear) → Timeout (60s) |

Do / Don't

| Do | Don't |
|----|-------|
| Always pass CancellationToken to ExecuteAsync | Let the pipeline ignore cancellation |
| Add UseJitter = true to retry strategies | Use fixed delays (thundering herd) |
| Log retries with OnRetry callback | Retry silently — you lose observability |
| Set ShouldHandle to specific exceptions | Handle all exceptions with default predicate in payment flows |
| Place timeout after retry for per-attempt timeout | Nest retry inside retry (doubles the attempt count unexpectedly) |
| Use AddStandardResilienceHandler for new HTTP clients | Hand-roll retry loops around HttpClient |
| Use circuit breaker for downstream service calls | Use circuit breaker for local in-memory operations |

See Also

  • skills/caching/SKILL.md — HybridCache stampede protection reduces load, complementing circuit breakers
  • skills/error-handling/SKILL.mdBrokenCircuitException should surface as a Result.Failure at domain boundaries

---

Source: https://github.com/zdanovichnick/dotnet-pilot
Author: zdanovichnick
Discovered via: skillsdirectory.com
Genre: ai-agents

SKILL.md source

---
name: Resilience
description: Polly v8 resilience patterns for .NET — retry, circuit breaker, timeout, hedging, and IHttpClientFactory integration using Microsoft.Extensions.Resilience.
---

# Resilience

Polly v8 resilience patterns for .NET — retry, circuit breaker, timeout, hedging, and IHttpClientFactory integration using Microsoft.Extensions.Resilience.

---
name: resilience
description: Polly v8 resilience patterns for .NET — retry, circuit breaker, timeout, hedging, and IHttpClientFactory integration using Microsoft.Extensions.Resilience.
---

# Resilience Patterns (Polly v8)

Reference for building fault-tolerant .NET services using Polly v8 and `Microsoft.Extensions.Resilience`. Used by `dnp-planner` and `dnp-tdd-developer-hard`.

## Package Reference

```xml
<PackageReference Include="Microsoft.Extensions.Resilience" Version="9.*" />
<!-- For HTTP clients: -->
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="9.*" />
```

**Breaking change from Polly v7**: the `Policy.Handle<>().Retry()` API is gone. Use `ResiliencePipelineBuilder` exclusively.

## Core Concepts

| Concept | Purpose |
|---------|---------|
| `ResiliencePipeline` | Executes a delegate through a chain of strategies |
| `ResiliencePipelineBuilder` | Fluent builder — add strategies in order (outermost first) |
| `ResiliencePipelineProvider<TKey>` | DI-resolved registry; resolve pipelines by key |
| `ResilienceContext` | Per-execution metadata (cancellation token, properties) |

Strategies execute in the order they are added. Outer strategies wrap inner ones — add timeout last to apply it per-attempt, or first to apply it to the whole pipeline.

## DI Registration

```csharp
builder.Services.AddResiliencePipeline("database", pipeline =>
    pipeline
        .AddRetry(new RetryStrategyOptions
        {
            MaxRetryAttempts = 3,
            Delay = TimeSpan.FromSeconds(1),
            BackoffType = DelayBackoffType.Exponential,
            UseJitter = true,
            ShouldHandle = new PredicateBuilder()
                .Handle<SqlException>()
                .Handle<TimeoutException>(),
            OnRetry = args =>
            {
                // args.Context, args.AttemptNumber, args.Outcome available
                Console.WriteLine($"Retry {args.AttemptNumber} after {args.RetryDelay}");
                return ValueTask.CompletedTask;
            }
        })
        .AddCircuitBreaker(new CircuitBreakerStrategyOptions
        {
            FailureRatio = 0.5,
            SamplingDuration = TimeSpan.FromSeconds(30),
            MinimumThroughput = 5,
            BreakDuration = TimeSpan.FromSeconds(15)
        })
        .AddTimeout(TimeSpan.FromSeconds(5)));
```

## Retry Strategy

```csharp
.AddRetry(new RetryStrategyOptions
{
    MaxRetryAttempts = 3,

    // Fixed, Linear, or Exponential
    BackoffType = DelayBackoffType.Exponential,
    Delay = TimeSpan.FromSeconds(1),  // base delay; doubles each attempt with Exponential

    // Adds ±20% random jitter to prevent thundering herd
    UseJitter = true,

    // Only retry on specific exceptions or results
    ShouldHandle = new PredicateBuilder()
        .Handle<HttpRequestException>()
        .HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.TooManyRequests),

    OnRetry = args =>
    {
        logger.LogWarning(
            "Retry attempt {Attempt} after {Delay}ms due to {Exception}",
            args.AttemptNumber,
            args.RetryDelay.TotalMilliseconds,
            args.Outcome.Exception?.Message);
        return ValueTask.CompletedTask;
    }
})
```

## Circuit Breaker

Opens the circuit when the failure ratio exceeds the threshold, stopping all calls for `BreakDuration`.

```csharp
.AddCircuitBreaker(new CircuitBreakerStrategyOptions
{
    // Open circuit if ≥50% of calls fail
    FailureRatio = 0.5,

    // Measurement window
    SamplingDuration = TimeSpan.FromSeconds(30),

    // Minimum calls in window before circuit can open
    MinimumThroughput = 5,

    // How long circuit stays open before moving to half-open
    BreakDuration = TimeSpan.FromSeconds(15),

    OnOpened = args =>
    {
        logger.LogError("Circuit opened for {Duration}s", args.BreakDuration.TotalSeconds);
        return ValueTask.CompletedTask;
    },
    OnClosed = _ =>
    {
        logger.LogInformation("Circuit closed — service recovered");
        return ValueTask.CompletedTask;
    }
})
```

When the circuit is open, calls throw `BrokenCircuitException` immediately without hitting the dependency.

## Timeout

```csharp
// Per-attempt timeout (placed after retry — each attempt gets its own timeout)
.AddTimeout(TimeSpan.FromSeconds(5))

// Or with options for callbacks
.AddTimeout(new TimeoutStrategyOptions
{
    Timeout = TimeSpan.FromSeconds(5),
    OnTimeout = args =>
    {
        logger.LogWarning("Operation timed out after {Timeout}", args.Timeout);
        return ValueTask.CompletedTask;
    }
})
```

## Hedging (Parallel Fallback Requests)

Sends a duplicate request after a delay if the first hasn't returned. Uses the first successful response.

```csharp
.AddHedging(new HedgingStrategyOptions<HttpResponseMessage>
{
    // Send a second request after 500ms if first hasn't returned
    Delay = TimeSpan.FromMilliseconds(500),

    // How many parallel hedged requests to allow
    MaxHedgedAttempts = 2,

    ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
        .HandleResult(r => !r.IsSuccessStatusCode),

    ActionGenerator = args => () =>
        ValueTask.FromResult(Outcome.FromResult(
            args.PrimaryContext.Properties.GetValue(
                new ResiliencePropertyKey<HttpResponseMessage>("hedged-result"),
                null!)))
})
```

Use hedging for latency-sensitive read operations where idempotency is guaranteed.

## IHttpClientFactory Integration

### Standard Resilience Handler (recommended shortcut)

Applies retry + circuit breaker + timeout + rate limiter in one call:

```csharp
builder.Services.AddHttpClient("payments", client =>
    {
        client.BaseAddress = new Uri(builder.Configuration["PaymentsApi:BaseUrl"]!);
    })
    .AddStandardResilienceHandler(options =>
    {
        options.Retry.MaxRetryAttempts = 3;
        options.CircuitBreaker.FailureRatio = 0.5;
        options.TotalRequestTimeout.Timeout = TimeSpan.FromSeconds(30);
    });
```

### Custom Pipeline per Client

```csharp
builder.Services.AddHttpClient("inventory")
    .AddResilienceHandler("inventory-pipeline", pipeline =>
    {
        pipeline
            .AddRetry(new RetryStrategyOptions<HttpResponseMessage>
            {
                MaxRetryAttempts = 2,
                ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
                    .Handle<HttpRequestException>()
                    .HandleResult(r => r.StatusCode >= HttpStatusCode.InternalServerError)
            })
            .AddTimeout(TimeSpan.FromSeconds(10));
    });
```

### Consuming the Named Client

```csharp
public class InventoryClient(IHttpClientFactory factory)
{
    public async Task<InventoryResponse?> GetStockAsync(string sku, CancellationToken ct)
    {
        var client = factory.CreateClient("inventory");
        var response = await client.GetAsync($"/stock/{sku}", ct);
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<InventoryResponse>(ct);
    }
}
```

## Named Pipeline Usage (Non-HTTP)

```csharp
public class OrderRepository(
    AppDbContext db,
    ResiliencePipelineProvider<string> pipelines,
    ILogger<OrderRepository> logger)
{
    public async Task<Order?> GetByIdAsync(int id, CancellationToken ct)
    {
        var pipeline = pipelines.GetPipeline("database");

        return await pipeline.ExecuteAsync(
            async token => await db.Orders.FindAsync([id], token),
            ct);
    }
}
```

## Recommended Pipeline Compositions

| Use Case | Strategies (outer → inner) |
|----------|---------------------------|
| Database queries | Retry (3x exponential) → Timeout (5s) |
| HTTP API calls | Retry (3x) → Circuit Breaker → Timeout (10s total) |
| Payment processing | Circuit Breaker → Timeout (30s) — no retry (idempotency risk) |
| Read-heavy low-latency | Hedging → Timeout |
| Background job step | Retry (5x linear) → Timeout (60s) |

## Do / Don't

| Do | Don't |
|----|-------|
| Always pass `CancellationToken` to `ExecuteAsync` | Let the pipeline ignore cancellation |
| Add `UseJitter = true` to retry strategies | Use fixed delays (thundering herd) |
| Log retries with `OnRetry` callback | Retry silently — you lose observability |
| Set `ShouldHandle` to specific exceptions | Handle all exceptions with default predicate in payment flows |
| Place timeout after retry for per-attempt timeout | Nest retry inside retry (doubles the attempt count unexpectedly) |
| Use `AddStandardResilienceHandler` for new HTTP clients | Hand-roll retry loops around `HttpClient` |
| Use circuit breaker for downstream service calls | Use circuit breaker for local in-memory operations |

## See Also
- `skills/caching/SKILL.md` — HybridCache stampede protection reduces load, complementing circuit breakers
- `skills/error-handling/SKILL.md` — `BrokenCircuitException` should surface as a `Result.Failure` at domain boundaries


---

**Source**: https://github.com/zdanovichnick/dotnet-pilot
**Author**: zdanovichnick
**Discovered via**: skillsdirectory.com
**Genre**: ai-agents

Related skills 6

running-claude-code-via-litellm-copilot

★ Featured

Use when routing Claude Code through a local LiteLLM proxy to GitHub Copilot, reducing direct Anthropic spend, configuring ANTHROPIC_BASE_URL or ANTHROPIC_MODEL overrides, or troubleshooting Copilot proxy setup failures such as model-not-found, no localhost traffic, or GitHub 401/403 auth errors.

xixu-me 155k
AI & ML

skills-cli

★ Featured

Use when users ask to discover, install, list, check, update, remove, back up, restore, sync, or initialize Agent Skills, mention `bunx skills`, `npx skills`, `skills.sh`, or `skills-lock.json`, ask "find a skill for X", or want help extending agent capabilities with installable skills.

xixu-me 155k
AI & ML

repo-intake-and-plan

★ Featured

Narrow RigorPilot helper for README-first deep learning repo reproduction. Use when the task is specifically to scan a repository, read the README and common project files, extract documented commands, classify inference, evaluation, and training candidates, and return the smallest trustworthy reproduction plan to the main orchestrator. Do not use for environment setup, asset download, command execution, final reporting, paper lookup, or end-to-end orchestration.

lllllllama 127k
AI & ML

image-to-video

★ Featured

Animate any still image on RunComfy — this skill is a smart router that matches the user's intent to the right i2v model in the RunComfy catalog. Picks HappyHorse 1.0 I2V (Arena #1, native audio, identity preservation) for general animations, Wan 2.7 with `audio_url` for custom-voiceover lip-sync, or Seedance 2.0 Pro for multi-modal animation from image + reference video + reference audio. Bundles each model's documented prompting patterns so the caller gets sharper output without burning ite...

agentspace-so 121k
AI & ML

video-edit

★ Featured

Edit existing video on RunComfy — this skill is a smart router that matches the user's intent to the right edit model in the RunComfy catalog. Picks Wan 2.7 Edit-Video (general restyle / background swap / packaging swap, identity + motion preservation), Kling 2.6 Pro Motion Control (transfer precise motion from a reference video to a target character), or Lucy Edit Restyle (lightweight identity-stable restyle / outfit swap). Bundles each model's documented prompting patterns so the skill gets...

agentspace-so 121k
AI & ML

nano-banana-2

★ Featured

Generate images with Google Nano Banana 2 (Gemini-family flash-tier text-to-image) on RunComfy — bundled with the model's documented prompting patterns so the skill gets sharper output than naive prompting against the same model. Documents Nano Banana 2's strengths (rapid iteration, in-image typography rendering, predictable framing, optional web-grounded context), the resolution-tier pricing, the safety-tolerance dial, and when to route to Nano Banana Pro / GPT Image 2 / Flux 2 / Seedream in...

agentspace-so 121k
AI & ML