Skip to content

// Resilience & Messaging · 05

Circuit Breaker

Failuresleads totripsOpenleads toClosed

Stop calling a service that's already drowning, so it can recover.

How it fits together

Circuit Breaker structure«client»Checkoutpayments clientClosedcalls flowcount failures«external»Payments serviceHalf-openone trial callOpenfails fast: 503callsfailures ≥ 50%after 15strial failstrial ok2 call2 trial call
uses / callsfails / error path· numbered steps replay in order
  • Checkout uses / calls Closed (calls)
  • Closed fails / error path Open (failures ≥ 50%)
  • Open uses / calls Half-open (after 15s)
  • Half-open fails / error path Open (trial fails)
  • Half-open uses / calls Closed (trial ok)
  • Closed uses / calls Payments service (2 call)
  • Half-open uses / calls Payments service (2 trial call)

Spot it

  • Every request waits out the full timeout
  • A dead dependency still gets hammered
  • Threads pile up behind one broken service

It costs

While open, even requests that would have succeeded are rejected.

In code

services.AddHttpClient<PaymentsClient>()    .AddResilienceHandler("payments", pipeline =>    {        pipeline.AddCircuitBreaker(new HttpCircuitBreakerStrategyOptions        {            FailureRatio = 0.5,            MinimumThroughput = 20,            SamplingDuration = TimeSpan.FromSeconds(30),            BreakDuration = TimeSpan.FromSeconds(15),            ShouldHandle = args => ValueTask.FromResult(                args.Outcome.Exception is not null                || (int?)args.Outcome.Result?.StatusCode is 408 or 429 or >= 500),        });    });
Go deeperThe full explanation and what to ask for · 1 min readExpand

Closed: calls flow and failures are counted. Open: calls fail immediately without touching the dependency. Half-open: after the break duration, a trial call decides whether to close again or reopen.

The classic mistakes are thresholds that never trip at low traffic (hence a minimum throughput) and counting the wrong failures: a 404 or a validation error says nothing about the health of the dependency. Use one breaker per downstream dependency, not one shared breaker for everything.

Retries absorb short blips; the breaker stops them from hammering a long outage. Pair it with a timeout, since a breaker only sees failures it is told about, and with a fallback such as cached data or a degraded feature.

Ask for it

  • closed / open / half-open
  • failure ratio
  • minimum throughput
  • sampling duration
  • break duration
  • fallback

Add a circuit breaker to the payments client: open when 50% of at least 20 calls fail within 30s, stay open 15s, then let one trial call through. While open, return 503 with Retry-After and log every state change.