This code shows how adding a circuit breaker pattern prevents repeated failed calls to a failing service, protecting the system from cascading failures. The before code lacks failure handling, while the after code tracks failures and opens the circuit to stop calls temporarily.
### Before: No circuit breaker, direct call
class ServiceClient:
def call_service(self):
response = external_service_request()
return response
### After: Circuit breaker applied
import time
class CircuitBreaker:
def __init__(self, failure_threshold=3, recovery_time=10):
self.failure_threshold = failure_threshold
self.recovery_time = recovery_time
self.failure_count = 0
self.last_failure_time = None
self.state = 'CLOSED'
def call(self, func, *args, **kwargs):
if self.state == 'OPEN':
if time.time() - self.last_failure_time > self.recovery_time:
self.state = 'HALF_OPEN'
else:
raise Exception('Circuit is open')
try:
result = func(*args, **kwargs)
self._reset()
return result
except Exception:
self._record_failure()
raise
def _record_failure(self):
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = 'OPEN'
def _reset(self):
self.failure_count = 0
self.state = 'CLOSED'
class ServiceClient:
def __init__(self):
self.circuit_breaker = CircuitBreaker()
def call_service(self):
return self.circuit_breaker.call(external_service_request)
### Explanation:
# The before code calls the external service directly, risking cascading failures.
# The after code wraps calls with a circuit breaker that stops calls after repeated failures,
# allowing the system to recover and avoid overload.