feat: hang-guard request() with an outer asyncio deadline
a backend can lose both its transfer wakeup and its own timeout enforcement in one failure (observed: a curl_cffi request that parked an aioweb consumer's event loop for 24h+ - loop idle, nothing in flight, the await never completing). wrap _raw_request in asyncio.wait_for at the effective timeout + 5s slack: the backend's own timeout still fires first in every healthy failure, the envelope only trips when the backend's timer is dead, and its cancellation unwedges the orphaned transfer. deadline = per-call numeric timeout else the session timeout (default 10); timeout=None from request_with_retries guards on the session bound (that was the wedge path). explicit Session(timeout=None) stays unguarded - the documented opt-out for genuinely unbounded calls. additive, no new knob. fired: a never-completing backend hangs forever without this, dies at deadline+slack with it, and request_with_retries returns a FailureResponse instead of hanging. Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
@@ -11,19 +11,19 @@ and swap the HTTP client while inheriting everything else.
|
||||
`requirements.txt`:
|
||||
|
||||
```
|
||||
aioweb @ git+ssh://git@git.rethinkstudios.io/rethink-public/aioweb.git@v1.0.2
|
||||
aioweb @ git+ssh://git@git.rethinkstudios.io/rethink-public/aioweb.git@v1.1.0
|
||||
```
|
||||
|
||||
Direct:
|
||||
|
||||
```bash
|
||||
pip install "aioweb @ git+ssh://git@git.rethinkstudios.io/rethink-public/aioweb.git@v1.0.2"
|
||||
pip install "aioweb @ git+ssh://git@git.rethinkstudios.io/rethink-public/aioweb.git@v1.1.0"
|
||||
```
|
||||
|
||||
Requires `aiohttp` and `yarl` (pulled transitively), plus the sibling `commons` library
|
||||
(a private `git+ssh` package — the install needs access to the `rethink-public` org).
|
||||
|
||||
Drop the `@v1.0.2` suffix from the line above to install the latest unpinned.
|
||||
Drop the `@v1.1.0` suffix from the line above to install the latest unpinned.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -150,6 +150,22 @@ Two changes can't be shimmed without re-introducing the bugs they fix:
|
||||
|
||||
## Changelog
|
||||
|
||||
### v1.1.0
|
||||
|
||||
- **Hang-guard on `request()` — a wedged backend can no longer park the loop forever.**
|
||||
`request()` now wraps the backend call (`_raw_request`) in an outer `asyncio.wait_for`
|
||||
deadline sized at the effective timeout **+ 5s slack** (`_HANG_GUARD_SLACK`). The
|
||||
backend's own timeout still fires first in every healthy failure (slack, not
|
||||
replacement); the envelope only trips when the backend's timer is dead — e.g. a
|
||||
`curl_cffi` transfer that loses both its wakeup and its own enforcement in the same
|
||||
failure — and its cancellation is what unwedges the orphaned transfer. The guard's
|
||||
deadline is the per-call numeric `timeout` when given, else the session timeout
|
||||
(`Session(timeout=...)`, default 10s); it composes cleanly with `request_with_retries`
|
||||
(a wedge becomes one logged `ServerTimeoutError`, the next attempt proceeds, and an
|
||||
exhausted retry returns a `FailureResponse` rather than hanging). **Opt-out:** a
|
||||
session deliberately constructed unbounded (`Session(timeout=None)`) stays unguarded,
|
||||
for genuinely long-lived calls (long-poll / streaming). Additive; no new knob.
|
||||
|
||||
### v0.1.13
|
||||
|
||||
- **Session-default timeout no longer poisons pooled keep-alive connections.**
|
||||
|
||||
Reference in New Issue
Block a user