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.