Skip to content

Commit b2a4cad

Browse files
lesnik512claude
andcommitted
docs(resilience): document async single-event-loop guard contract
resilience.md described the sync/async sharing rule but not the cross-loop constraint: an AsyncBulkhead / AsyncCircuitBreaker binds to the first event loop it runs on and raises RuntimeError if later driven from a different loop. Document it on the Bulkhead paragraph (with a pointer to _event_loop_guard.py) and cross-reference from CircuitBreaker. Surfaced by the delta audit as a pre-existing gap; behavior unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 8618a24 commit b2a4cad

1 file changed

Lines changed: 2 additions & 2 deletions

File tree

architecture/resilience.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ The decision logic — status/method eligibility, streaming-body refusal, exhaus
1010

1111
## Bulkhead
1212

13-
`Bulkhead` / `AsyncBulkhead` is a concurrency limiter. `AsyncBulkhead` uses `asyncio.Semaphore` with a bounded acquire wait; sync `Bulkhead` uses `threading.Semaphore`. A sync instance cannot share with an async one. Both are sharable across clients (one instance = one shared concurrency pool).
13+
`Bulkhead` / `AsyncBulkhead` is a concurrency limiter. `AsyncBulkhead` uses `asyncio.Semaphore` with a bounded acquire wait; sync `Bulkhead` uses `threading.Semaphore`. A sync instance cannot share with an async one. Both are sharable across clients (one instance = one shared concurrency pool). An `AsyncBulkhead` additionally binds to the first event loop it runs on; driving it from a second event loop raises `RuntimeError` rather than silently splitting its semaphore across loops (the shared single-event-loop guard, `_event_loop_guard.py`).
1414

1515
## CircuitBreaker + AsyncTimeout
1616

17-
`AsyncCircuitBreaker` and sync `CircuitBreaker` are a classic consecutive-failure circuit breaker: the circuit opens after `failure_threshold` consecutive counted failures, fast-fails while OPEN, admits one probe after `reset_timeout` (HALF_OPEN), and closes again after `success_threshold` consecutive probe successes; a probe failure re-opens it. A *counted failure* is a `NetworkError`, an httpware `TimeoutError`, or a `StatusError` whose `status_code` is in the effective failure set (default: all 5xx, 500–599); 4xx including 429 count as successes, and any other exception type propagates unchanged without affecting circuit state. When the breaker refuses a request — OPEN, or HALF_OPEN with the single probe slot already taken — it raises `CircuitOpenError` and never forwards to `next`; the error's `retry_after` carries the seconds until the next probe will be admitted, or `None` when a concurrent probe is already in flight. A breaker instance is sharable across clients (one shared circuit); a sync instance cannot be shared with an async one.
17+
`AsyncCircuitBreaker` and sync `CircuitBreaker` are a classic consecutive-failure circuit breaker: the circuit opens after `failure_threshold` consecutive counted failures, fast-fails while OPEN, admits one probe after `reset_timeout` (HALF_OPEN), and closes again after `success_threshold` consecutive probe successes; a probe failure re-opens it. A *counted failure* is a `NetworkError`, an httpware `TimeoutError`, or a `StatusError` whose `status_code` is in the effective failure set (default: all 5xx, 500–599); 4xx including 429 count as successes, and any other exception type propagates unchanged without affecting circuit state. When the breaker refuses a request — OPEN, or HALF_OPEN with the single probe slot already taken — it raises `CircuitOpenError` and never forwards to `next`; the error's `retry_after` carries the seconds until the next probe will be admitted, or `None` when a concurrent probe is already in flight. A breaker instance is sharable across clients (one shared circuit); a sync instance cannot be shared with an async one. Like `AsyncBulkhead`, an `AsyncCircuitBreaker` binds to the first event loop it runs on and raises `RuntimeError` if later driven from a different one.
1818

1919
Both `AsyncCircuitBreaker` and `CircuitBreaker` expose a read-only `state` property that returns a public `CircuitState` enum (`CLOSED`/`OPEN`/`HALF_OPEN`), importable from `httpware`, for health checks and introspection. The property is a raw read of the stored state: because the OPEN→HALF_OPEN transition is lazy (it fires on the next request after `reset_timeout` elapses, not on a clock tick), `state` continues to report `OPEN` until a request is actually admitted as the probe — reading the property never triggers the transition.
2020

0 commit comments

Comments
 (0)