From 4937b96da9be23e04b6f0972ad20328850356b89 Mon Sep 17 00:00:00 2001 From: beam Date: Wed, 28 Oct 2026 07:55:53 +0000 Subject: [PATCH] beam: circuit_breaker HALF_OPEN single-probe gating --- wizard-work/beam/circuit_breaker.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/wizard-work/beam/circuit_breaker.py b/wizard-work/beam/circuit_breaker.py index f2df694..248024f 100644 --- a/wizard-work/beam/circuit_breaker.py +++ b/wizard-work/beam/circuit_breaker.py @@ -47,7 +47,7 @@ class CircuitBreaker: self._consecutive_failures = 0 self._consecutive_successes = 0 self._opened_at: Optional[float] = None - self._half_open_probe: bool = False + self._probe_in_flight = False self._lock = threading.Lock() @property @@ -61,7 +61,6 @@ class CircuitBreaker: if self._clock() - self._opened_at >= self._recovery_timeout: self._state = CircuitState.HALF_OPEN self._consecutive_successes = 0 - self._half_open_probe = False def _on_success(self) -> None: with self._lock: @@ -73,7 +72,6 @@ class CircuitBreaker: self._consecutive_failures = 0 self._consecutive_successes = 0 self._opened_at = None - self._half_open_probe = False else: self._consecutive_failures = 0 @@ -85,7 +83,6 @@ class CircuitBreaker: self._opened_at = self._clock() self._consecutive_successes = 0 self._consecutive_failures = 0 - self._half_open_probe = False return self._consecutive_failures += 1 if self._consecutive_failures >= self._failure_threshold: @@ -99,16 +96,20 @@ class CircuitBreaker: if self._state is CircuitState.OPEN: raise CircuitOpenError("circuit is open") if self._state is CircuitState.HALF_OPEN: - if self._half_open_probe: - raise CircuitOpenError("circuit is half-open (probe in flight)") - self._half_open_probe = True + if self._probe_in_flight: + raise CircuitOpenError("circuit is open (probe in flight)") + self._probe_in_flight = True try: result = fn(*args, **kwargs) except BaseException: self._on_failure() raise - self._on_success() - return result + else: + self._on_success() + return result + finally: + with self._lock: + self._probe_in_flight = False def reset(self) -> None: with self._lock: @@ -116,4 +117,4 @@ class CircuitBreaker: self._consecutive_failures = 0 self._consecutive_successes = 0 self._opened_at = None - self._half_open_probe = False \ No newline at end of file + self._probe_in_flight = False \ No newline at end of file