Notes / Patterns / 04 Microservice Patterns / 07 Circuit Breaker

07 — Circuit Breaker

A state machine (Closed → Open → Half-Open) that stops calls to a failing dependency before they cascade. The foundational resilience pattern for distributed systems.

Updated June 30, 2026 · §202606301405-8 ·

07 — Circuit Breaker

Interview level: Principal / Staff (L6/L7) — appears in every “design for resilience” question. The L5 answer names the pattern; the L6/L7 answer covers the state machine, the threshold calibration, the distributed state problem, and the half-open probe strategy.


Context

Service A calls Service B synchronously. B becomes slow or unavailable — GC pause, database overload, network partition. Without a circuit breaker, A’s threads pile up waiting for B’s timeout, exhausting A’s connection pool and thread pool. A becomes slow, which cascades to Service C calling A. One dependency failure takes down the call graph.


Problem

ForceDescription
Cascading failureA slow dependency consumes caller threads; caller becomes slow; its callers become slow
Wasted resourcesRequests queued behind a broken dependency consume memory, threads, and connection slots
Recovery interferenceA recovering dependency is overwhelmed by the backlog of retries from all callers
Slow timeoutsDefault TCP timeouts (minutes) are far too long; threads block for the full window

Solution

stateDiagram-v2
    [*] --> Closed

    Closed --> Open : failure rate > threshold\n(e.g. > 50% over last 10 calls)
    Open --> HalfOpen : sleep window elapsed\n(e.g. 30 seconds)
    HalfOpen --> Closed : probe request succeeds
    HalfOpen --> Open : probe request fails

    state Closed {
        [*] --> Monitoring
        Monitoring : count successes + failures\nin sliding window
    }
    state Open {
        [*] --> Rejecting
        Rejecting : fail-fast all calls\nreturn cached / fallback immediately
    }
    state HalfOpen {
        [*] --> Probing
        Probing : allow 1 request through\nwait for result
    }

State definitions

StateBehaviourExit condition
ClosedAll calls pass through; count outcomes in sliding windowFailure rate > threshold → Open
OpenAll calls fail-fast immediately; no network call madeSleep window expires → Half-Open
Half-OpenAllow one probe request throughSuccess → Closed; Failure → Open

Threshold calibration

The two knobs that matter most:

failure_threshold:  50%      # open when >50% of calls in window fail
window_size:        10 calls # minimum sample size before tripping
sleep_window:       30s      # how long to stay Open before probing
  • Too sensitive (low threshold, small window): the breaker trips on transient blips — a single GC pause opens the circuit and cuts traffic for 30 seconds unnecessarily.
  • Too sluggish (high threshold, large window): the breaker only trips after 1000 failed calls, by which time the caller’s thread pool is already exhausted.
  • Calibrate from measured P99 and error rate baselines, not from defaults.

The Distributed State Problem

A single-process circuit breaker (Hystrix, Resilience4j) maintains state in memory on one instance. At 100 instances of Service A, each has its own independent circuit breaker for Service B. An instance that hasn’t hit the threshold stays Closed while others have already Opened.

Instance A-1: 60% error rate → OPEN   ✓ stops traffic
Instance A-2: 30% error rate → CLOSED ✗ still hammering broken B
Instance A-3: 45% error rate → CLOSED ✗ still hammering broken B

Solutions:

Option 1 — Accept the inconsistency. Each instance independently protects itself. Eventually all instances open (within seconds of each other). Acceptable for most use cases.

Option 2 — Shared state in Redis. Centralise the sliding window counter in Redis. All instances share the failure count; when threshold is hit, all open simultaneously. Adds a Redis dependency to the breaker itself — if Redis is slow, the breaker adds latency to every call.

Option 3 — Load-balancer level. Envoy/Linkerd 01 — Sidecar implements outlier detection at the proxy level. One proxy per pod, but the control plane coordinates ejection across pods via xDS. Effectively gives fleet-wide circuit breaking without a shared state store. This is the preferred approach in a service mesh.


Fallback Strategies

An open circuit must return something. Options ranked by user impact:

FallbackQualityWhen to use
Cached last-known-good responseStale but usefulRead-heavy; staleness is acceptable
Degraded response (partial data)ReducedSome data is better than none
Empty / default responseMinimalCaller can handle absence gracefully
Error to callerNoneCaller must handle failure; fail-fast is better than lying

Never silently return empty as if it were a valid response — callers can’t distinguish “no data” from “error”. Return HTTP 503 with a Retry-After header, or a structured error with a circuit_open flag.


Consequences

Gains

  • Fail-fast eliminates thread/connection exhaustion on the caller side
  • Recovery window lets the dependency heal before being re-hit at full load
  • Fallback path makes failures visible and predictable instead of invisible and cascading

Trade-offs

  • State machine adds complexity; incorrect thresholds cause spurious trips or false safety
  • Half-open probe must be representative; a cheap probe that always succeeds while real traffic fails gives false confidence
  • Distributed state problem means per-instance breakers can diverge significantly under partial failure
  • Not a substitute for timeouts — always set aggressive timeouts in addition to the circuit breaker; the breaker is the second line of defence

Observability

circuit_breaker_state{service, dependency}          # 0=closed, 1=open, 2=half-open
circuit_breaker_requests_total{state, outcome}      # calls by state and result
circuit_breaker_fallback_total{reason}              # fallback invocations
circuit_breaker_open_duration_seconds               # how long the circuit stayed open
circuit_breaker_failure_rate{dependency}            # current rolling failure rate

Alert on circuit_breaker_state == 1 (open) for > 60 seconds — this means the dependency hasn’t recovered within the sleep window and may need intervention. Correlate with the dependency’s own error rate metric to confirm the root cause is in the dependency, not the breaker threshold.


MAANG Interview Anchors

  • “The circuit breaker is the second line of defence after the timeout. Timeouts bound how long a single call blocks; the circuit breaker prevents the 1000th call from being made after the first 500 have timed out. Without both, you have incomplete protection.”

  • “At 100 instances, distributed circuit breaker state is a real problem. My default answer is to push the circuit-breaking logic into the sidecar proxy (Envoy outlier detection) because the control plane gives fleet-wide coordination without a shared state store dependency.”

  • “The half-open probe must be a real request, not a health-check ping. A dependency can pass a /health check while still failing real queries — I’ve seen this when a database primary is up but a table lock prevents actual writes. The probe must exercise the actual call path.”

  • “Calibrate thresholds from data, not defaults. I’d run a week of baseline metrics, look at P99 error rate and P99 latency for each dependency, and set the threshold at 3σ above the baseline error rate. A threshold set below normal variability will produce alert fatigue from spurious trips.”


Relation to Fan-Out

In a 04 — Fan-Out / Fan-In, a per-shard circuit breaker stops the dispatcher from retrying into a shard that’s already failing consistently, which breaks the feedback loop where retries make the failure worse and trigger even more retries. Without it, one bad shard out of N can consume a disproportionate share of the fan-out’s retry budget while contributing nothing to the merged result.


Known Uses

SystemImplementation
Netflix HystrixOriginal circuit breaker library; now in maintenance mode
Resilience4jJVM replacement for Hystrix; supports sliding window by count or time
Envoy outlier detectionProxy-level circuit breaking via consecutive 5xx ejection
LinkerdPer-route failure accrual; exponential backoff on ejected backends
AWS SDKBuilt-in retry + circuit breaker via RetryMode: adaptive

Local graph

Full graph →

Linked from 14 notes

Q6 Answer — Retry Storm

Worked answer to Fan-Out/Fan-In Practice Q6: how uncapped per-worker retries turn a transient blip into a full outage, and the retry policy that prevents it.

04 — Fan-Out / Fan-In

Decompose a request into parallel sub-tasks (fan-out), execute concurrently, then merge results (fan-in). The foundational pattern for latency-bound aggregation.

08 — Retry with Exponential Backoff and Jitter

Retry transient failures with exponentially increasing wait times and randomised jitter to prevent thundering-herd recovery storms. The foundational pattern for resilient RPC.

09 — Bulkhead

Partition resources (thread pools, connection pools, semaphores) so that a failure or overload in one partition cannot exhaust resources for others. Limits blast radius.

01 — Sidecar

Co-locate a helper container with the application container to handle cross-cutting concerns — TLS, observability, auth, retries — without modifying application code.

Platform & Cloud Maturity Model (L1 → L5)

This model defines maturity progression across core platform and cloud architecture pillars.

Retry Policies and the Delivery Semantics They Produce

Every retry decision is made along three axes — trigger, backoff, budget — before delivery semantics even enter the picture. At-least-once, at-most-once, and exactly-once are the accumulated side effect of those decisions at every hop, not a separate design choice.

2 — Connection Management

Connection pools, keep-alive, timeouts, retries, and circuit breakers as the client-side levers for managing unreliable network connections.

11 — Reliability Patterns (Microservice Building Blocks)

Timeout, Rate Limiter, Fallback, and Adaptive Concurrency — the remaining resilience building blocks beyond Circuit Breaker, Retry, Bulkhead, and Hedged Requests, which already have their own chapters in this book.

Patterns

A book-shaped table of contents for reusable engineering patterns spanning object-oriented design, enterprise architecture, distributed systems, messaging, APIs, cloud infrastructure, observability, security, concurrency, AI/agentic systems, and organizational design — grounded in production experience at scale.

Resilience patterns

Application-level failure handling in Signal Forge: retries, circuit breakers, backoff, and delivery-safety patterns for downstream dependency failures.

Service: gateway-api

gateway-api's endpoints, domain model, configuration, OTel instrumentation, resilience patterns, and failure modes.