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
{{ message }}
Repository navigation
Commit a45e0eb
Browse filesBrowse the repository at this point in the historyBrowse files
* fix!: validate Retry, AsyncRetry and RetryBudget config at construction
* fix: match retry_methods case-insensitively
* fix: emit retry.giving_up when Retry-After exceeds max_delay
* fix: treat a Retry-After integer too large for a float as an infinite wait
Copy file name to clipboardExpand all lines: docs/observability.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,6 +13,8 @@ The logger and event names below are a stable public contract:
13
13
14
14
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.
15
15
16
+
`retry.giving_up` fires when a retryable failure is re-raised without another attempt, and its `reason` attribute says why. `max_attempts` means every attempt failed. `retry_after_exceeds_max_delay` means the server's `Retry-After` asked for a longer wait than `max_delay` allows; these events also carry `retry_after` and `max_delay` in seconds, and `retry_after` is `inf` when the header held an integer too large to fit in a float. Both kinds carry `attempts`, `method`, `url`, `last_status` and `last_exception_type`.
17
+
16
18
Events that carry a `url` attribute get it redacted first: httpware strips `user:pass@` userinfo and replaces every query-parameter value with `REDACTED`, keeping the keys. A URL logged as `https://api.test/items?page=REDACTED&sort=REDACTED` came from `https://api.test/items?page=2&sort=name`.
Copy file name to clipboardExpand all lines: docs/resilience.md
+10-8Lines changed: 10 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -24,11 +24,11 @@ from httpware.middleware.resilience import AsyncRetry
24
24
| Parameter | Default | Effect |
25
25
|---|---|---|
26
26
|`max_attempts`|`3`| Total tries, including the first. `1` disables retries; `<1` raises `ValueError`. |
27
-
|`base_delay`|`0.1` (s) | Floor for the full-jitter exponential backoff. |
28
-
|`max_delay`|`5.0` (s) | Ceiling for backoff. |
27
+
|`base_delay`|`0.1` (s) | Floor for the full-jitter exponential backoff. Must be `>= 0`; a negative or NaN value raises `ValueError`. |
28
+
|`max_delay`|`5.0` (s) | Ceiling for backoff and the longest `Retry-After` honored. Must be finite and `>= 0`; a negative, NaN or infinite value raises `ValueError`. |
29
29
|`retry_status_codes`|`frozenset({408, 429, 502, 503, 504})`| Status codes considered retryable. |
30
-
|`retry_methods`|`frozenset({"GET", "HEAD", "OPTIONS", "PUT", "DELETE"})`| Idempotent methods only. To retry POST, pass a frozenset that includes `"POST"`. |
31
-
|`respect_retry_after`|`True`| When a retryable response has a `Retry-After` header, wait that long instead of the jittered backoff. If the value is over `max_delay`, `AsyncRetry` gives up and re-raises the `StatusError` with a PEP 678 note: `httpware: Retry-After (Ns) exceeded max_delay (Ms); giving up`. To avoid this, set `respect_retry_after=False` or raise `max_delay`. |
30
+
|`retry_methods`|`frozenset({"GET", "HEAD", "OPTIONS", "PUT", "DELETE"})`| Idempotent methods only. To retry POST, pass a frozenset that includes `"POST"`. Matching ignores case, so `"post"` works too. |
31
+
|`respect_retry_after`|`True`| When a retryable response has a `Retry-After` header, wait that long instead of the jittered backoff. If the value is over `max_delay`, `AsyncRetry` gives up, emits `retry.giving_up` with `reason="retry_after_exceeds_max_delay"`, and re-raises the `StatusError` with a PEP 678 note: `httpware: Retry-After (Ns) exceeded max_delay (Ms); giving up`. To avoid this, set `respect_retry_after=False` or raise `max_delay`. |
32
32
|`budget`|`RetryBudget()`| The token bucket. Pass one shared `RetryBudget` to several clients to give them a joint budget. |
33
33
34
34
To bound the total time across all attempts, put [`AsyncTimeout`](#asynctimeout) first in the chain. To bound a single request, set `httpx2.Timeout` on the client or pass `timeout=` per request.
@@ -40,7 +40,7 @@ To bound the total time across all attempts, put [`AsyncTimeout`](#asynctimeout)
- an HTTP date (RFC 5322): `Retry-After: Wed, 21 Oct 2026 07:28:00 GMT` waits until that time, or not at all if it has passed.
42
42
43
-
Either form over `max_delay` makes `AsyncRetry` give up as described above. Negative integers count as 0. Malformed values are ignored and the jittered backoff is used instead.
43
+
Either form over `max_delay` makes `AsyncRetry` give up as described above. An integer too large to fit in a float counts as an infinite wait, so it always exceeds `max_delay`. Negative integers count as 0. Malformed values are ignored and the jittered backoff is used instead.
44
44
45
45
### Streaming-body refusal
46
46
@@ -70,9 +70,11 @@ A token bucket in the style of Finagle's retry budget. Each request deposits a t
70
70
71
71
| Parameter | Default | Effect |
72
72
|---|---|---|
73
-
|`ttl`|`10.0` (s) | Sliding window over which deposits and withdrawals count. |
74
-
|`min_retries_per_sec`|`10.0`| Floor: at least this many retries per second are allowed, whatever the deposit rate. |
75
-
|`percent_can_retry`|`0.2`| Fraction of recent deposits that can convert to retries (above the floor). |
73
+
|`ttl`|`10.0` (s) | Sliding window over which deposits and withdrawals count. Must be finite and `> 0`. |
74
+
|`min_retries_per_sec`|`10.0`| Floor: at least this many retries per second are allowed, whatever the deposit rate. Must be finite and `>= 0`. |
75
+
|`percent_can_retry`|`0.2`| Fraction of recent deposits that can convert to retries (above the floor). Must be finite and `>= 0`; values above `1.0` allow more retries than requests. |
76
+
77
+
An out-of-range value raises `ValueError` when the budget is constructed.
0 commit comments