पाठ 7 / 25

How a Circuit Breaker Works

Describe the closed, open and half-open states and the thresholds that move between them.

Stop calling what is broken

A circuit breaker, popularised by Michael Nygard's book Release It!, wraps calls to a dependency and tracks their outcomes. In the closed state calls pass through normally while the breaker records failures (errors, timeouts and optionally slow calls) in a sliding window (the last N calls or the last N seconds). When the failure rate crosses a threshold (for example 50% of at least 20 calls), the breaker opens: calls fail immediately with an error or fallback, without touching the dependency. This protects the caller's resources and gives the dependency room to recover. After a wait duration (say 30 seconds), the breaker moves to half-open and lets a small number of trial calls through. If they succeed, it closes; if they fail, it opens again for another wait period. Breakers turn a slow, resource-draining failure into a fast, cheap one.

Closed, open, half-open

Failures open the breaker; after a wait, trial calls decide whether it closes again.

Three circles arranged in a triangle connected by curved arrows, one circle solid, one hollow, one half filled.
Figure 3.1 — The circuit breaker state machine.

A minimal circuit breaker

Count-based window, failure-rate threshold and a half-open trial.

import time
from collections import deque

class CircuitOpen(Exception): pass

class CircuitBreaker:
    def __init__(self, window=20, min_calls=10, failure_rate=0.5, open_seconds=30):
        self.results = deque(maxlen=window)
        self.min_calls, self.threshold, self.open_seconds = min_calls, failure_rate, open_seconds
        self.state, self.opened_at = "closed", 0.0

    def call(self, fn):
        if self.state == "open":
            if time.monotonic() - self.opened_at < self.open_seconds:
                raise CircuitOpen()                      # fail fast
            self.state = "half_open"                     # allow a trial call
        try:
            result = fn()
        except Exception:
            self._record(False)
            raise
        self._record(True)
        return result

    def _record(self, ok):
        if self.state == "half_open":
            self.state = "closed" if ok else "open"
            self.opened_at = time.monotonic()
            self.results.clear()
            return
        self.results.append(ok)
        failures = self.results.count(False)
        if len(self.results) >= self.min_calls and failures / len(self.results) >= self.threshold:
            self.state, self.opened_at = "open", time.monotonic()

Require a minimum number of calls

Without a minimum, two failures out of the first three calls after a deploy would open the breaker. A minimum call count prevents tripping on tiny samples.

त्वरित जाँच: What happens to calls while a circuit breaker is open?

  • They fail fast (or use a fallback) without calling the dependency
  • They are queued until the dependency recovers
  • They are retried immediately
  • They are sent to a random service
Answer

They fail fast (or use a fallback) without calling the dependency — An open breaker short-circuits calls to protect both caller and dependency.