Skip to main content

Use a circuit breaker around a failing dependency

2026-11-11 16:36:27.520355+00:00
Use a circuit breaker around a failing dependency

When a dependency is failing repeatedly, every queued job can spend time making the same doomed call. A circuit breaker temporarily stops new attempts and allows a controlled probe after a cooldown.

The single-process demonstration opens after a failure and allows one probe after its cooldown.

Make the state visible

This small local model accepts an explicit clock so the transitions can be tested without sleeping. It opens after one failure purely to keep the demonstration short.

class Circuit:
    def __init__(self, cooldown=10):
        self.cooldown = cooldown
        self.retry_at = None
        self.probing = False

    def allow(self, now):
        if self.retry_at is None:
            return True
        if now < self.retry_at or self.probing:
            return False
        self.probing = True
        return True

    def success(self):
        self.retry_at = None
        self.probing = False

    def failure(self, now):
        self.retry_at = now + self.cooldown
        self.probing = False

circuit = Circuit()
assert circuit.allow(0)
circuit.failure(0)
assert not circuit.allow(5)
assert circuit.allow(10)
assert not circuit.allow(10)
circuit.success()
assert circuit.allow(11)

The retry_at value represents an open circuit. Once the cooldown passes, allow() admits one probe and blocks the next until success() or failure() resolves it. Calling success closes the circuit.

The caller must report a result on every admitted path, including timeouts. If a probe is abandoned without reporting, this simple instance can remain stuck in its probing state.

Choose the scope and failure signals

This class is an instructional single-process model. It has no thread synchronization, shared persistence, rolling failure window or probe-expiry recovery. Several worker processes each constructing it would operate independent circuits.

Decide whether failures should be tracked per provider, per account or per operation. An invalid recipient is usually not evidence that every request to a provider should stop. A global breaker triggered by one user's bad input can deny unrelated work.

An open circuit also needs a business outcome. A job might be deferred until a later attempt, marked unavailable or offered a manual path. Do not report success simply because the provider call was skipped.

A circuit breaker controls admission. It does not provide idempotency, a rate limit or queue capacity. Those remain separate controls even when the same request passes through all of them.

Postzyo provides related dependency context. RFC 9110 documents HTTP failure semantics; the breaker thresholds and state model above are application design choices.