Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ _Avoid_: adapter, which names the third-party object a decoder builds and memoiz
**Counted failure**:
The circuit breaker's unit of account: a `NetworkError`, an httpware `TimeoutError`, or a
`StatusError` whose status is in the effective failure set (default: all 5xx). Every other 4xx —
429 included — counts as a *success*, and any other exception type propagates without touching
circuit state. "Failure" alone is ambiguous here: a failed request is very often not a counted
failure.
429 included — counts as a *success*. A `RetryBudgetExhaustedError` is classified by its
`last_exception`, and any other exception type propagates without touching circuit state. "Failure"
alone is ambiguous here: a failed request is very often not a counted failure.

**Circuit key**:
The value a keyed circuit breaker computes from a request to pick its circuit; requests with equal
Expand Down
2 changes: 1 addition & 1 deletion docs/observability.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The logger and event names below are a stable public contract:
| `httpware.circuit_breaker` | `circuit.opened` (WARNING), `circuit.rejected` (WARNING), `circuit.half_open` (INFO), `circuit.closed` (INFO) |
| `httpware.timeout` | `timeout.exceeded` (WARNING) |

Each log record has an `event` field holding the event name, such as `event="circuit.opened"`, which you can filter on in your log aggregator. Events from `AsyncKeyedCircuitBreaker` and `KeyedCircuitBreaker` also carry `circuit_key`, the circuit they belong to. [Resilience](resilience.md) describes when each event fires.
Each log record has an `event` field holding the event name, such as `event="circuit.opened"`, which you can filter on in your log aggregator. `circuit.rejected` is emitted at most once per second per circuit, so an open circuit under heavy traffic does not flood your logs. The first rejection of each outage is always emitted, `suppressed` counts the rejections left out since the previous one, and `circuit.closed` reports the outage's total in `rejected`. Events from `AsyncKeyedCircuitBreaker` and `KeyedCircuitBreaker` also carry `circuit_key`, the circuit they belong to. [Resilience](resilience.md) describes when each event fires.

```python
import logging
Expand Down
12 changes: 10 additions & 2 deletions docs/resilience.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ A circuit breaker counts consecutive failures and, past a threshold, stops sendi
- OPEN: until `reset_timeout` has passed, every request fails at once with `CircuitOpenError`, whose `retry_after` says how many seconds remain. The first request after that moves the circuit to HALF_OPEN and becomes the probe.
- HALF_OPEN: one probe at a time is let through. After `success_threshold` successful probes in a row the circuit closes; one failed probe opens it again.

An outcome only counts against the state its request was admitted under. A request that is still in flight when the circuit changes state, such as a slow call sent before the circuit opened, finishes without touching the circuit. It cannot reopen a HALF_OPEN circuit, count toward `success_threshold`, or free the probe slot.

### Constructor

| Parameter | Default | Effect |
Expand All @@ -176,6 +178,8 @@ A circuit breaker counts consecutive failures and, past a threshold, stops sendi

A counted failure is a `NetworkError`, an httpware `TimeoutError`, or a `StatusError` whose status is in `failure_status_codes`. Other exceptions pass through without changing the circuit.

A `RetryBudgetExhaustedError` from a retry placed after the breaker is classified by its `last_exception`. If the last attempt failed with a counted failure, the breaker counts one; if it failed with a 4xx, that is a success.

4xx responses, 429 included, count as successes. A 429 means the service is up and throttling you; opening the circuit on it would add circuit-open rejections on top of the throttling.

### `CircuitOpenError`
Expand All @@ -189,9 +193,13 @@ Emitted on logger `httpware.circuit_breaker`:
| Event | When |
|---|---|
| `circuit.opened` | The failure threshold was reached and the circuit went from CLOSED to OPEN. |
| `circuit.rejected` | A request failed fast because the circuit was OPEN or a HALF_OPEN probe was already running. |
| `circuit.rejected` | A request failed fast because the circuit was OPEN or a HALF_OPEN probe was already running. Emitted at most once per second per circuit, and always for the first rejection of an outage. Its `suppressed` attribute counts the rejections in the same outage that were not emitted since the previous `circuit.rejected`. |
| `circuit.half_open` | The reset timeout passed and the circuit went from OPEN to HALF_OPEN. |
| `circuit.closed` | The success threshold was reached and the circuit went from HALF_OPEN to CLOSED. |
| `circuit.closed` | The success threshold was reached and the circuit went from HALF_OPEN to CLOSED. Its `rejected` attribute is the number of requests rejected during the outage, emitted or not. |

An outage runs from the moment the circuit opens until it closes again; a probe failure that reopens it stays in the same outage.

The breaker emits its events after releasing its internal lock, so a logging handler may send requests through a client that uses the same breaker.

### Time-based failure-rate mode

Expand Down
Loading
Loading