The Runtime Theory
hardreference#circuit-breaker#state-machine#fail-fast#timeout

Design a Circuit Breaker

Implement a circuit breaker with closed, open, and half-open states that fails fast on a degraded dependency and recovers automatically.

The Runtime Theory Team2 min read
Solve it

Solving happens on the judge — come back and mark it done

Sample cases

in5 of 10 calls fail within the window, threshold is 50%

outCircuit transitions from closed to open

inCooldown expires and the probe call succeeds

outCircuit transitions from open to half-open, then to closed

Design a circuit breaker for a service that calls a downstream HTTP dependency. The breaker must track failure rate, trip when the threshold is breached, fail fast while open, and recover via half-open probe calls.

What to decide

  1. Failure detection window — How far back do you look? A sliding window (last 10 seconds) adapts faster than a fixed window but uses more memory. A 1-minute window smooths out bursts but delays trip detection.

  2. Failure threshold — 50% of requests failing within the window is common, but the right number depends on the service's normal error rate. A payment service with a 0.1% baseline error rate should trip at 5%; a search endpoint with a 5% baseline should trip at 50%.

  3. Cooldown period — How long to wait in the open state before probing? Too short and you hammer a recovering dependency with retries. Too long and you deny service unnecessarily. A randomized cooldown (30-90 seconds) avoids synchronized recovery storms.

  4. Half-open probe — Send exactly one request through the breaker. If it succeeds, close the circuit. If it fails, reopen. Multiple concurrent probes from multiple threads need a lock or semaphore to avoid double-open.

Implementation sketch

python
class CircuitBreaker:
    def __init__(self, failure_threshold, cooldown, window):
        self.state = "closed"  # closed | open | half_open
        self.failures = deque()  # (timestamp, outcome) within window
        self.threshold = failure_threshold
        self.cooldown = cooldown
        self.window = window
 
    def call(self, fn, *args, **kwargs):
        if self.state == "open":
            if time.time() - self.opened_at > self.cooldown:
                self.state = "half_open"
                return self._attempt(fn, *args, **kwargs)
            raise CircuitOpenException()
 
        try:
            result = fn(*args, **kwargs)
            self._record_success()
            return result
        except Exception as e:
            self._record_failure()
            raise
 
    def _attempt(self, fn, *args, **kwargs):
        try:
            result = fn(*args, **kwargs)
            self.state = "closed"
            self.failures.clear()
            return result
        except Exception as e:
            self.state = "open"
            self.opened_at = time.time()
            raise
 
    def _record_failure(self):
        now = time.time()
        self.failures.append((now, True))
        self.failures = [f for f in self.failures if f[0] > now - self.window]
        if len(self.failures) / max(1, len(self.failures)) > self.threshold:
            self.state = "open"
            self.opened_at = now
 
    def _record_success(self):
        now = time.time()
        self.failures.append((now, False))
        self.failures = [f for f in self.failures if f[0] > now - self.window]

Read the circuit breaker article for the design model, and the cross-region failover trace to see how circuit breakers drive failover decisions in production.

More practice in this topic

One dispatch a week

The trace behind each problem, the tradeoff that explains it, and one technical dispatch per week — no noise.

One technical dispatch per week. No noise.

Not started

Sign in to save your learning progress.

Sign in to save