← Concepts

阅读中文版本

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.

resilience

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

https://martinfowler.com/bliki/CircuitBreaker.html

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.