Circuit Breaker
A circuit breaker stops sending requests to a failing dependency after a threshold, failing fast while the dependency recovers instead of exhausting callers with timeouts.
Definition
A circuit breaker is a state machine that wraps calls to a remote dependency. In the closed state requests flow normally and failures are counted. When failures cross a threshold, the breaker opens and subsequent calls fail immediately without contacting the dependency. After a cooldown, it enters a half-open state and allows a limited probe; success closes the breaker, while failure reopens it. The goal is to prevent cascading failure and give the dependency room to recover.
Context and scope
Circuit breakers are useful when a dependency has meaningful downtime or latency spikes, such as a third-party API or a database. They are not a substitute for timeouts and retries; they complement them by stopping retry storms once the dependency is known to be unhealthy.
Common misconceptions
- A circuit breaker retries failed requests — it fails fast; retries belong to a separate policy
- Opening the breaker fixes the dependency — it protects callers and buys recovery time, but the dependency still needs repair
- One failure should open the breaker — thresholds and sampling matter, otherwise a single blip disables the path
Anti-patterns
- Using a breaker without per-call timeouts, so the open-state check itself waits on a hung connection
- Sharing one breaker across unrelated dependencies, causing healthy calls to be blocked by an unrelated failure
- Counting expected business errors as infrastructure failures
Related concepts
Related examples
Authoritative source
Why failing fast helps
When a dependency hangs, callers wait for timeouts, threads or connection pools fill, and the failure spreads to healthy paths. A circuit breaker converts repeated slow failures into immediate local failures. The caller can then degrade gracefully, serve a cached value, or return a clear error without consuming more resources.
The state machine
type State = 'closed' | 'open' | 'half-open';
class CircuitBreaker {
private state: State = 'closed';
private failures = 0;
private openedAt = 0;
constructor(
private readonly threshold = 5,
private readonly cooldownMs = 30_000,
) {}
async call<T>(operation: () => Promise<T>): Promise<T> {
if (this.state === 'open') {
if (Date.now() - this.openedAt < this.cooldownMs) {
throw new Error('circuit open');
}
this.state = 'half-open';
}
try {
const result = await operation();
this.failures = 0;
this.state = 'closed';
return result;
} catch (error) {
this.failures += 1;
if (this.state === 'half-open' || this.failures >= this.threshold) {
this.state = 'open';
this.openedAt = Date.now();
}
throw error;
}
}
}
Choosing thresholds
Thresholds should reflect the dependency’s normal error rate and the caller’s tolerance. Count only infrastructure failures, not validation errors. Use a rolling window or consecutive-failure count, and make the cooldown long enough for the dependency to recover but short enough to detect recovery.
When to use it
Use a breaker around remote dependencies with partial failure modes: payment providers, search services, legacy APIs. Compared to a retry policy, a breaker reduces load during failure, while retries increase it; combine them so retries happen only while the breaker is closed. Compared to a rate limiter, the breaker reacts to failures, not traffic volume.
Pitfalls
A breaker that opens forever strands traffic; always define half-open probing. A breaker per host can hide a regional outage; a breaker per dependency can over-trip on one bad instance. Instrument state transitions and failure reasons so operators can distinguish an outage from a misconfigured threshold.