In a microservices ecosystem, service failure is contagious.
Suppose Service A calls Service B, which depends on an external shipping carrier API. If the shipping API begins timing out with 30-second delays, requests in Service B pile up. Threads stay blocked waiting for sockets, database connection pools exhaust, and Service B's memory spikes until it crashes.
Now, Service A's connection pools exhaust waiting for Service B, and the outage cascades all the way to your customer checkout page!
To halt cascading collapses, resilient systems implement the Circuit Breaker Pattern.
The Circuit Breaker State Machine
+-------------------------------------------------+
| |
v |
+------------+ Failure Threshold Exceeded +----------+---+
| CLOSED | ----------------------------> | OPEN |
+------------+ +--------------+
^ |
| Success Threshold Met | Sleep Window Expires
| v
+------------+ +--------------+
| HALF-OPEN | <---------------------------- | (Testing) |
+------------+ +--------------+
| ^
+---------- Failure Encountered --------------+
- CLOSED (Normal Operation): Requests pass through to the downstream service.
- OPEN (Fast Failure): All requests to the dependency are immediately rejected locally without making a network call!
- HALF-OPEN (Trial Probe): Permits a small percentage of probe requests. If successful, resets to CLOSED.
Python Circuit Breaker Implementation
import time
import threading
from typing import Callable, Any
class CircuitBreakerOpenException(Exception):
pass
class CircuitBreaker:
def __init__(self, failure_threshold: int = 5, recovery_time_seconds: float = 30.0):
self.failure_threshold = failure_threshold
self.recovery_time = recovery_time_seconds
self.state = "CLOSED"
self.failure_count = 0
self.last_failure_time = 0.0
self.lock = threading.Lock()
def call(self, func: Callable, *args, **kwargs) -> Any:
with self.lock:
now = time.time()
if self.state == "OPEN":
if now - self.last_failure_time > self.recovery_time:
self.state = "HALF-OPEN"
else:
raise CircuitBreakerOpenException("Circuit is OPEN. Service unavailable.")
try:
result = func(*args, **kwargs)
with self.lock:
if self.state == "HALF-OPEN":
self.state = "CLOSED"
self.failure_count = 0
return result
except Exception as e:
with self.lock:
self.failure_count += 1
self.last_failure_time = time.time()
if self.state in ("CLOSED", "HALF-OPEN") and self.failure_count >= self.failure_threshold:
self.state = "OPEN"
raise e
Real-World Usage with Fallbacks
import requests
cb = CircuitBreaker(failure_threshold=3, recovery_time_seconds=10.0)
def fetch_shipping_rates(weight: float):
resp = requests.get(f"https://api.carrier.com/rates?weight={weight}", timeout=2.0)
resp.raise_for_status()
return resp.json()
def get_rates_with_fallback(weight: float):
try:
return cb.call(fetch_shipping_rates, weight)
except (CircuitBreakerOpenException, requests.RequestException):
return {"rate": 15.00, "source": "fallback_estimate"}

Top comments (0)