How to implement a circuit breaker in Python

A Python CircuitBreaker class that tracks failures, opens after a threshold, and retries after a timeout.

Medium Python 3.9+ Aug 9, 2026 Microservices patterns 13 views 0 copies

Python code

44 lines
Python 3.9+
class 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

stdout
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

  1. Use a decorator to wrap function calls automatically
  2. 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

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Microservices patterns

Related tutorials and quizzes for this topic.