How to implement a circuit breaker in Python
A Python CircuitBreaker class that tracks failures, opens after a threshold, and retries after a timeout.
Python code
44 linesclass CircuitBreaker:
def __init__(self, failure_threshold=3, timeout=5):
self.failure_threshold = failure_threshold
self.timeout = timeout
self.failure_count = 0
self.last_failure_time = None
self.state = "CLOSED"
def call(self, mock_downstream):
if self.state == "OPEN":
if self._timeout_elapsed():
self.state = "HALF_OPEN"
else:
return "Service unavailable (circuit open)"
try:
result = mock_downstream()
if self.state == "HALF_OPEN":
self.failure_count = 0
self.state = "CLOSED"
return result
except Exception:
self.failure_count += 1
self.last_failure_time = __import__("time").time()
if self.state == "HALF_OPEN" or self.failure_count >= self.failure_threshold:
self.state = "OPEN"
return "Call failed"
def _timeout_elapsed(self):
import time
return (time.time() - self.last_failure_time) > self.timeout
def mock_downstream_v1():
raise Exception("Downstream error")
def mock_downstream_v2():
return "Success response"
if __name__ == "__main__":
circuit = CircuitBreaker(failure_threshold=2, timeout=1)
print(circuit.call(mock_downstream_v1)) # Call failed
print(circuit.call(mock_downstream_v1)) # Call failed, opens circuit
print(circuit.call(mock_downstream_v2)) # Service unavailable (circuit open)
Output
Call failed
Call failed
Service unavailable (circuit open)
How it works
The CircuitBreaker maintains a state machine: CLOSED (normal), OPEN (rejects calls), and HALF_OPEN (probe). The call method wraps the downstream mock; on success it resets the failure count and closes the circuit, on exception it increments the counter and opens after the threshold. The timeout check uses time.time() to transition OPEN to HALF_OPEN, allowing a trial call. This prevents cascading failures in microservices by failing fast when a dependency is unhealthy.
Common mistakes
- Forgetting to reset the failure count after a successful HALF_OPEN call
- Not using a timestamp, causing the timeout to never trigger
- Sharing one CircuitBreaker instance across unrelated downstream services
Variations
- Use a decorator to wrap function calls automatically
- Add exponential backoff for the OPEN state's retry interval
Real-world use cases
- Wrapping HTTP calls to a third-party API to avoid hammering it during outages.
- Protecting a database client from connection failures in a high-traffic backend.
- Isolating failures when calling internal microservices in a complex request chain.
Sponsored
More from Microservices patterns
- BFF aggregation pattern: combine multiple service responses in Python easy
- Backward Compatible Schema Evolution in Python medium
- Bulkhead Thread Pool per Service Mock in Python medium
- CQRS with Separate Read and Write Repositories in Python medium
- Cache-Aside Pattern in Python: Per-Service Mock easy
- Consumer Driven Contract Pact Mock in Python medium
Keep learning
Related tutorials and quizzes for this topic.