You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Each log record carries an `event` field with the event-name string (e.g. `event="circuit.opened"`), usable for log-aggregator filtering. See [resilience.md](resilience.md) for the full event tables per middleware.
16
+
Each log record carries an `event` field with the event-name string (e.g. `event="circuit.opened"`), usable for log-aggregator filtering. Events from `AsyncKeyedCircuitBreaker` / `KeyedCircuitBreaker` also carry `circuit_key`, naming the circuit they belong to. See [resilience.md](resilience.md) for the full event tables per middleware.
from httpware.middleware.resilience import AsyncKeyedCircuitBreaker # async
261
+
from httpware.middleware.resilience import KeyedCircuitBreaker # sync
262
+
```
263
+
264
+
Use a keyed breaker when one client sends requests to more than one upstream. A plain `AsyncCircuitBreaker` holds a single circuit, so one failing upstream fast-fails requests to all the healthy ones. The keyed breaker keeps a separate circuit for each circuit key and opens only the failing upstream's circuit.
265
+
266
+
Each circuit behaves exactly like an [`AsyncCircuitBreaker`](#asynccircuitbreaker-circuitbreaker) built with the same arguments: the same states, failure classification, rate mode, half-open probe and events. Each circuit has its own probe slot, so two upstreams that recover at the same time are probed independently.
267
+
268
+
### Constructor
269
+
270
+
Every `AsyncCircuitBreaker` parameter, with the same defaults, plus:
271
+
272
+
| Parameter | Default | Effect |
273
+
|---|---|---|
274
+
|`key`| the request's origin | Maps a request to its circuit key. Any hashable value works. The default is the origin as a string built from scheme, host and port, such as `https://a.example` or `http://a.example:8080`. Hosts are lowercased and default ports dropped, so `https://A.example/x` and `https://a.example:443/y` share a circuit, while `http://a.example` and `https://a.example:8443` each get their own. Userinfo is never part of it. |
275
+
276
+
### Circuit lifetime
277
+
278
+
A circuit is created on the first request for its key and kept for as long as the breaker lives. Nothing is evicted or pruned, so memory grows with the number of distinct keys. `key` must therefore map requests to a small, bounded set, such as your configured upstreams. Never derive it from user input. A key function that returns something unbounded, such as the full URL, grows the map forever.
279
+
280
+
### Observability
281
+
282
+
The keyed breakers emit the same events as `AsyncCircuitBreaker` on the same `httpware.circuit_breaker` logger. Each event has one extra attribute, `circuit_key`, which is the `str()` of the request's circuit key. httpware redacts `url` but not `circuit_key`, which reaches log records and span events exactly as the key function returned it. A custom `key` must not return credentials, tokens or other sensitive values. The default origin contains none.
283
+
284
+
### Example
285
+
286
+
```python
287
+
from httpware import AsyncClient
288
+
from httpware.middleware.resilience import AsyncKeyedCircuitBreaker, AsyncRetry
await client.get("https://suggest-b.example/v1/suggest") # unaffected if suggest-a is down
296
+
```
297
+
298
+
In the [composition](#composition), the keyed breaker goes where the circuit breaker goes. It sits outside `AsyncRetry` and counts one outcome per retry sequence. Sync usage is identical: `Client` + `KeyedCircuitBreaker`, no `await`.
0 commit comments