Resilience

codewithmukesh/dotnet-claude-kit/skills/resilience

作者 codewithmukesh23300897f4d1無授權條款754 個星標收錄於 2026年10月8日更新於 2026年10月8日儲存庫2 個月前更新

Resilience patterns for .NET 10 applications using Polly v8. Covers retry, circuit breaker, timeout, fallback, rate limiter, hedging, and composing resilience pipelines. Load this skill when implementing retry logic, circuit breakers, handling transient failures, or when the user mentions "Polly", "resilience", "retry", "circuit breaker", "timeout", "fallback", "rate limit", "hedging", "transient fault", "HttpClient resilience", or "resilience pipeline".

AI 產生的概覽

指導 .NET 10 開發者使用 Polly v8 實作重試、斷路器、逾時、後援、對沖和限流等復原模式。

功能
此技能為使用 Polly v8 在 .NET 10 應用程式中建構復原能力提供參考指引和程式碼範例。內容涵蓋重試、斷路器、逾時、後援、限流器、對沖以及組合復原管線,並包含反模式和決策指南。它產出的是實作指引,而非可執行的產出項目。
適用情境
在 .NET 應用程式中實作重試邏輯、斷路器、逾時、後援、限流或對沖時使用。它也適用於處理暫時性失敗,或使用者提到 Polly、復原管線、HttpClient 復原或暫時性錯誤的情境。
執行需求
需要 .NET 10 和 Polly v8,包括用於 HTTP 復原處理常式的 Microsoft.Extensions.Http.Resilience。不隨附指令碼,僅為指示性內容。

Resilience

Core Principles

  1. Polly v8 resilience pipelines, not v7 policies — Polly v8 replaced Policy with ResiliencePipeline. Never use PolicyBuilder, Policy.Handle<>(), or ISyncPolicy. The new API is composable, type-safe, and integrates natively with IHttpClientFactory.
  2. Configure via AddResilienceHandler, not manual wrapping — For HTTP calls, use Microsoft.Extensions.Http.Resilience which adds pipelines directly to HttpClient via DI. No manual ExecuteAsync wrapping.
  3. Compose strategies, don't nest them — A single ResiliencePipeline can chain retry + circuit breaker + timeout. Strategies execute outer-to-inner (first added = outermost). No need for nested try/catch or manual orchestration.
  4. Always set timeouts — Every external call needs a timeout. Use Polly's AddTimeout() as the innermost strategy so it applies per-attempt, and optionally an outer timeout for total elapsed time.
  5. Instrument everything — Polly v8 emits Metering events and supports TelemetryOptions for OpenTelemetry. Use them to monitor retry rates, circuit breaker state, and timeout frequency.

Patterns

HTTP Client Resilience (Recommended Default)

csharp
// Program.cs — Standard resilience handler covers 90% of use casesbuilder.Services.AddHttpClient<IPaymentGateway, PaymentGatewayClient>(client =>{    client.BaseAddress = new Uri("https://api.payments.example.com");}).AddStandardResilienceHandler(); // Retry + circuit breaker + timeout out of the box
// That's it. The standard handler configures:// - Retry: 3 attempts, exponential backoff, jitter// - Circuit breaker: 10% failure ratio over 30s sampling, 30s break// - Attempt timeout: 10s per attempt// - Total request timeout: 30s

Why: AddStandardResilienceHandler() from Microsoft.Extensions.Http.Resilience applies production-ready defaults. Override only when you need different thresholds.

Custom HTTP Resilience Configuration

csharp
builder.Services.AddHttpClient<ICatalogService, CatalogServiceClient>(client =>{    client.BaseAddress = new Uri("https://api.catalog.example.com");}).AddResilienceHandler("catalog", builder =>{    // Total timeout — outermost, caps total elapsed time    builder.AddTimeout(TimeSpan.FromSeconds(15));
    // Retry — exponential backoff with jitter    builder.AddRetry(new HttpRetryStrategyOptions    {        MaxRetryAttempts = 3,        BackoffType = DelayBackoffType.Exponential,        UseJitter = true,        Delay = TimeSpan.FromMilliseconds(500),        ShouldHandle = static args => ValueTask.FromResult(            args.Outcome.Result?.StatusCode is HttpStatusCode.RequestTimeout                or HttpStatusCode.TooManyRequests                or HttpStatusCode.ServiceUnavailable                || args.Outcome.Exception is HttpRequestException)    });
    // Circuit breaker — prevent cascading failures    builder.AddCircuitBreaker(new HttpCircuitBreakerStrategyOptions    {        FailureRatio = 0.5,        SamplingDuration = TimeSpan.FromSeconds(10),        MinimumThroughput = 10,        BreakDuration = TimeSpan.FromSeconds(30)    });
    // Per-attempt timeout — innermost    builder.AddTimeout(TimeSpan.FromSeconds(5));});

Why: Named resilience handlers let you tune per-service. The order matters: total timeout > retry > circuit breaker > attempt timeout.

Non-HTTP Resilience Pipeline

csharp
// For database calls, message queues, or any non-HTTP operationbuilder.Services.AddResiliencePipeline("database", builder =>{    builder        .AddRetry(new RetryStrategyOptions        {            MaxRetryAttempts = 3,            BackoffType = DelayBackoffType.Exponential,            Delay = TimeSpan.FromMilliseconds(200),            ShouldHandle = new PredicateBuilder()                .Handle<TimeoutException>()                .Handle<InvalidOperationException>(ex =>                    ex.Message.Contains("deadlock", StringComparison.OrdinalIgnoreCase))        })        .AddTimeout(TimeSpan.FromSeconds(10));});
// Inject and usepublic sealed class OrderRepository(    AppDbContext db,    [FromKeyedServices("database")] ResiliencePipeline pipeline){    public async Task<Order?> GetByIdAsync(Guid id, CancellationToken ct)    {        return await pipeline.ExecuteAsync(            async token => await db.Orders.FindAsync([id], token),            ct);    }}

Why: AddResiliencePipeline registers a named pipeline in DI. Inject with [FromKeyedServices] for clean, testable code.

Typed Resilience Pipeline

csharp
// When the operation returns a specific type, use ResiliencePipeline<T>builder.Services.AddResiliencePipeline<string, HttpResponseMessage>("external-api", builder =>{    builder        .AddFallback(new FallbackStrategyOptions<HttpResponseMessage>        {            FallbackAction = static args =>            {                var response = new HttpResponseMessage(HttpStatusCode.OK)                {                    Content = new StringContent("{\"status\":\"degraded\",\"data\":[]}")                };                return Outcome.FromResultAsValueTask(response);            },            ShouldHandle = static args => ValueTask.FromResult(                args.Outcome.Exception is not null                || args.Outcome.Result?.IsSuccessStatusCode == false)        })        .AddRetry(new RetryStrategyOptions<HttpResponseMessage>        {            MaxRetryAttempts = 2,            Delay = TimeSpan.FromMilliseconds(500)        })        .AddTimeout(TimeSpan.FromSeconds(5));});

Why: Typed pipelines let you add fallback strategies that return a default value when all retries are exhausted — critical for graceful degradation.

Hedging (Parallel Requests)

csharp
builder.Services.AddHttpClient<ISearchService, SearchServiceClient>()    .AddResilienceHandler("search-hedging", builder =>    {        builder.AddHedging(new HttpHedgingStrategyOptions        {            MaxHedgedAttempts = 2,            Delay = TimeSpan.FromMilliseconds(500) // Send parallel request after 500ms        });        builder.AddTimeout(TimeSpan.FromSeconds(3));    });

Why: Hedging sends a parallel request if the first hasn't responded within the delay. Use for latency-sensitive reads where you can tolerate duplicate work.

Telemetry Integration

csharp
builder.Services.AddResiliencePipeline("monitored", (builder, context) =>{    // Polly v8 emits metrics via System.Diagnostics.Metrics automatically.    // ConfigureTelemetry wires structured logging for strategy events.    builder        .ConfigureTelemetry(new TelemetryOptions        {            LoggerFactory = context.ServiceProvider.GetRequiredService<ILoggerFactory>()        })        .AddRetry(new RetryStrategyOptions { MaxRetryAttempts = 3 })        .AddCircuitBreaker(new CircuitBreakerStrategyOptions())        .AddTimeout(TimeSpan.FromSeconds(10));});
// In Program.cs — wire up OpenTelemetry to capture Polly metricsbuilder.Services.AddOpenTelemetry()    .WithMetrics(metrics => metrics.AddMeter("Polly"));

Rate Limiting (.NET Built-in)

.NET provides built-in rate limiting middleware via AddRateLimiter() — no external packages needed. Algorithms: AddFixedWindowLimiter, AddSlidingWindowLimiter, AddTokenBucketLimiter, AddConcurrencyLimiter.

csharp
builder.Services.AddRateLimiter(options =>{    options.AddFixedWindowLimiter("fixed", opt =>    {        opt.PermitLimit = 100;        opt.Window = TimeSpan.FromSeconds(60);        opt.QueueLimit = 0;    });
    // Always return ProblemDetails with Retry-After on 429    options.OnRejected = async (context, ct) =>    {        context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;        if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))            context.HttpContext.Response.Headers.RetryAfter =                ((int)retryAfter.TotalSeconds).ToString();        await context.HttpContext.Response.WriteAsJsonAsync(            new ProblemDetails { Title = "Too many requests", Status = 429 }, ct);    };});
app.UseRateLimiter();app.MapGet("/api/orders", ListOrders).RequireRateLimiting("fixed");

Anti-patterns

BAD: Using Polly v7 API

csharp
// BAD — v7 policy syntax, do not usevar retryPolicy = Policy    .Handle<HttpRequestException>()    .WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)));
var response = await retryPolicy.ExecuteAsync(() => httpClient.GetAsync("/api/data"));

GOOD: Polly v8 Resilience Pipeline

csharp
// GOOD — v8 pipeline via DIbuilder.Services.AddHttpClient<IDataService, DataServiceClient>()    .AddStandardResilienceHandler();

BAD: Wrapping Every Call Manually

csharp
// BAD — manual resilience per call sitepublic async Task<Order> GetOrderAsync(Guid id){    try    {        return await _pipeline.ExecuteAsync(async ct =>            await _httpClient.GetFromJsonAsync<Order>($"/orders/{id}", ct));    }    catch (TimeoutRejectedException)    {        return Order.Empty;    }    catch (BrokenCircuitException)    {        return Order.Empty;    }}

GOOD: Pipeline Handles Everything via HttpClient DI

csharp
// GOOD — resilience is configured at the HttpClient levelpublic async Task<Order?> GetOrderAsync(Guid id, CancellationToken ct){    var response = await _httpClient.GetAsync($"/orders/{id}", ct);    if (!response.IsSuccessStatusCode) return null;    return await response.Content.ReadFromJsonAsync<Order>(ct);}

BAD: Retry on Non-Idempotent Operations

csharp
// BAD — retrying a POST that creates a resource risks duplicatesbuilder.AddRetry(new RetryStrategyOptions{    MaxRetryAttempts = 5 // This will create 5 orders on transient failures!});

GOOD: Retry Only Idempotent Operations or Use Idempotency Keys

csharp
// GOOD — use idempotency key header for non-idempotent operationsbuilder.AddRetry(new HttpRetryStrategyOptions{    MaxRetryAttempts = 3,    ShouldHandle = static args => ValueTask.FromResult(        args.Outcome.Result?.StatusCode is HttpStatusCode.RequestTimeout            or HttpStatusCode.ServiceUnavailable)});
// Pair with idempotency key in the requesthttpClient.DefaultRequestHeaders.Add("Idempotency-Key", Guid.NewGuid().ToString());

BAD: Circuit Breaker Without Monitoring

csharp
// BAD — circuit breaker with no visibility into state changesbuilder.AddCircuitBreaker(new CircuitBreakerStrategyOptions());// How do you know when it trips? You don't.

GOOD: Circuit Breaker with Telemetry

csharp
// GOOD — Polly v8 metrics captured via OpenTelemetrybuilder.Services.AddOpenTelemetry()    .WithMetrics(metrics => metrics.AddMeter("Polly"));
// Dashboard alerts on: polly.circuit_breaker.state = Open

Decision Guide

ScenarioStrategyConfiguration
HTTP calls to external APIsAddStandardResilienceHandler()Use defaults, override only specific thresholds
HTTP with custom thresholdsAddResilienceHandler("name", ...)Named handler with per-service tuning
Database / EF Core callsAddResiliencePipeline("db", ...)Retry on deadlock/timeout, no circuit breaker
Message queue publishingAddResiliencePipeline("mq", ...)Retry with exponential backoff, timeout
Latency-sensitive readsAddHedging(...)Parallel request after delay threshold
Graceful degradationAddFallback(...)Return cached/default value on total failure
Per-attempt time limitAddTimeout(...) innermost2-10s depending on operation
Total operation time limitAddTimeout(...) outermostSum of all retries + buffer
Non-idempotent writesRetry with idempotency keyOr no retry — fail fast
Read-heavy microserviceStandard handler + hedgingLow latency with redundancy
API rate limitingAddRateLimiter() + RequireRateLimiting()Fixed, sliding, or token bucket per endpoint

來源與署名

來源:codewithmukesh/dotnet-claude-kit位於skills/resilience提交2330089

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架

更多來自 codewithmukesh/dotnet-claude-kit 的技能

Wrap Up

codewithmukesh

在 session 結束時把已完成工作、待辦事項與經驗寫入交接檔案,並在 session 開始時重新載入。

Productivity & Workflow7542 個月前更新

Workflow Mastery

codewithmukesh

Claude Code workflow mastery for .NET developers. Covers parallel execution with git worktrees, plan mode strategy, verification loops, auto-formatting hooks, permission setup for dotnet CLI, prompting techniques, subagent patterns, and context discipline — token budget management, MCP-first navigation, lazy loading, and subagent isolation — all adapted for the .NET ecosystem. Load this skill when setting up Claude Code for a .NET project, optimizing workflows, running parallel sessions, when context is running low or sessions feel sluggish, when exploring a large codebase efficiently, or when the user mentions "productivity", "workflow", "parallel", "worktree", "plan mode", "permissions", "hooks", "10x", "setup Claude Code", "speed up development", "context", "tokens", "budget", "running out of context", "too many files", or "large codebase". Inspired by tips from Boris Cherny (creator of Claude Code) and the Anthropic team.

待分類7542 個月前更新

Vertical Slice

codewithmukesh

指導 .NET 開發者以垂直切片架構組織應用程式,涵蓋功能資料夾、端點分組與處理常式模式。

Software Development7542 個月前更新

Testing

codewithmukesh

Testing strategy for .NET 10 applications. Covers xUnit v3, WebApplicationFactory for integration tests, Testcontainers for real database testing, Verify for snapshot testing, and the AAA pattern. Load this skill when writing tests, setting up test infrastructure, reviewing test coverage, or when the user mentions "test", "xUnit", "WebApplicationFactory", "Testcontainers", "integration test", "unit test", "bUnit", "snapshot test", "Verify", "test coverage", "AAA pattern", "WireMock", or "FakeTimeProvider".

待分類7542 個月前更新

Tdd

codewithmukesh

Guided test-driven development workflow for .NET 10 using xUnit v3, WebApplicationFactory, Testcontainers, and Verify snapshots. Follows the strict red-green-refactor cycle. Use when: "TDD", "test-driven", "let's TDD this", "red green refactor", "write the test first", or when building a feature with clear acceptance criteria.

待分類7542 個月前更新

Spec

codewithmukesh

透過結構化提問,把模糊的功能想法轉化為雙方確認並持久化的規格文件。

Productivity & Workflow7542 個月前更新