0.10.0 — circuit breaker + async timeout
httpware 0.10.0 — circuit breaker + async timeout
Minor release. Additive only — no breaking changes.
New public names
from httpware.middleware.resilience import AsyncCircuitBreaker # async
from httpware.middleware.resilience import CircuitBreaker # sync
from httpware.middleware.resilience import AsyncTimeout
from httpware import CircuitOpenErrorAsyncCircuitBreaker / CircuitBreaker
Classic consecutive-failure circuit breaker. Counts counted failures (5xx, NetworkError, TimeoutError) and fast-fails with CircuitOpenError once failure_threshold consecutive failures are observed. Recovers via a HALF_OPEN probe after reset_timeout seconds; closes when success_threshold consecutive probe successes are seen.
4xx responses — including 429 — count as successes. A 429 means healthy-but-throttling; tripping the circuit on it would amplify incidents.
CircuitOpenError (a ClientError subclass) carries retry_after: float | None — the seconds until the next probe window (None when HALF_OPEN with a probe already in flight).
Sharable across multiple clients (one shared circuit). A sync CircuitBreaker cannot be shared with an AsyncCircuitBreaker.
AsyncTimeout
Bounds total wall-clock across the inner pipeline — including retries and backoff sleeps. Raises httpware.TimeoutError on expiry. Async-only: sync Python has no cancellation primitive that can interrupt a blocking call mid-flight.
New observability events
| Logger | Event | When |
|---|---|---|
httpware.circuit_breaker |
circuit.opened |
Failure threshold reached |
httpware.circuit_breaker |
circuit.rejected |
Request fast-failed (OPEN or HALF_OPEN probe taken) |
httpware.circuit_breaker |
circuit.half_open |
Reset timeout elapsed; probe admitted |
httpware.circuit_breaker |
circuit.closed |
Success threshold reached; service recovered |
httpware.timeout |
timeout.exceeded |
Overall timeout expired |
Recommended chain ordering
AsyncTimeout → AsyncCircuitBreaker → AsyncBulkhead → AsyncRetry → terminal
Breaker outside retry: an open circuit short-circuits the whole retry loop; the breaker counts one outcome per fully-exhausted retry sequence.