Repository navigation
Rewind file bodies when internally retrying a request - #13330
Conversation
Merging this PR will regress 0 benchmarks
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ⚡ | test_client_receive_large_websocket_compressed_messages[isal.isal_zlib] |
86.5 ms | 72 ms | +20.04% |
| 👁 | test_resolve_dynamic_resource_url_with_many_dynamic_routes_with_common_prefix |
532.8 ms | 582.6 ms | -8.56% |
Tip
Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.
Comparing aiolibsbot:koan/implement-13329 (7005508) with master (6264834)
Footnotes
-
83 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
|
The fix looks right. extending it to the case where the request is retried should work. Just didn't plan for that case |
Retry path now mirrors 307/308 path: reuses |
|
|
Rebase with requested adjustmentsBranch Changes applied
Not changed (and why)
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
530625f to
2a7cf1a
Compare
|
|
Rebase with requested adjustmentsBranch Changes applied
Not changed (and why)
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
|
|
Rebase with requested adjustmentsBranch Changes applied
Not changed (and why)
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
|
|
Rebase with requested adjustmentsBranch Changes applied
Not changed (and why)
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
|
|
Rebase with requested adjustmentsBranch Changes applied
Not changed (and why)
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
PR Review — Rewind file bodies when internally retrying a requestBoth outstanding maintainer requests are now addressed and the fix holds up under verification — merge-ready with nits. The core change is the right shape: reusing the failed attempt's payload instead of rebuilding it from The two
✅ Resolved since last review (1)Previously-flagged issues verified fixed
🟢 Suggestions
1. `await req._close()` can swap the retryable error for the writer task's exception
|
Rebase with requested adjustmentsBranch Changes applied
Not changed (and why)
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
|
Note No code changes since the last review ( |
Retrying a request after ClientOSError/ServerDisconnectedError rebuilt the payload from the original `data`. For a file object the handle had already been advanced by the aborted write, so the retry resent only the unread remainder with a matching (short) Content-Length. Reuse the payload from the failed attempt instead, as the redirect path already does: IOBasePayload seeks back to the position it recorded before the first attempt. Bodies that cannot be replayed are no longer retried; the original connection error propagates rather than a silently truncated body being sent. AsyncIterablePayload now marks itself consumed as soon as it starts draining an uncached iterator, so an interrupted write is correctly reported as unreplayable instead of only after StopAsyncIteration.
Rebase with requested adjustmentsBranch Changes applied
Not changed (and why)
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
ef3a00d to
785249e
Compare
Co-authored-by: Sam Bull <aa6bs0@sambull.org>
Backport to 3.14: 💔 cherry-picking failed — conflicts found❌ Failed to cleanly apply b084084 on top of patchback/backports/3.14/b08408400fc1a3566ea284f7b07cd54543dcd370/pr-13330 Backporting merged PR #13330 into master
🤖 @patchback |
Backport to 3.15: 💔 cherry-picking failed — conflicts found❌ Failed to cleanly apply b084084 on top of patchback/backports/3.15/b08408400fc1a3566ea284f7b07cd54543dcd370/pr-13330 Backporting merged PR #13330 into master
🤖 @patchback |
|
@aiolibsbot create the backports |
This is causing permafailures: ``` public/logs/chain_of_trust.log was corrupted in transit: the storage backend rejected the body as not matching Content-MD5 +r5pkJPuO/JWdjABXz6gEQ==. Retrying. ``` I'm guessing the md5 calculation is mismatched somehow. It's probably not worth debugging considering aiohttp merged a fix upstream (aio-libs/aiohttp#13330), and the issue this was attempting to work around is so rare.

What do these changes do?
When
_requestinternally retries after aClientOSError/ServerDisconnectedError, itrebuilt the payload from the original
data. Ifdatawas an open file handle, theaborted write had already advanced it, so the retry resent only the unread remainder
(with a matching, short
Content-Length) — the server silently received a truncatedupload.
The retry path now reuses the payload from the failed attempt, exactly as the 307/308
redirect path already does.
IOBasePayload._set_or_restore_start_position()seeks thehandle back to the position recorded before the first attempt, so the full body is resent.
AsyncIterablePayloadadditionally marks itself consumed as soon as it starts draining anuncached iterator, rather than only on
StopAsyncIteration. Previously an interruptedwrite left
consumedasFalsewhile the iterator was partially drained, which made ahalf-sent body look replayable to both the retry and the redirect path.
Are there changes in behavior for the user?
Yes, two:
truncated one.
longer retried. The original connection error propagates instead of a silently truncated
body being sent.
Is it a substantial burden for the maintainers to support this?
No. It reuses the payload-replay mechanism the redirect path already relies on, rather
than adding a parallel one.
Related issue number
Fixes #13329
Checklist
retry path; behaviour change is described in the news fragment)
CONTRIBUTORS.txt— N/A(already a contributor)
CHANGES/folderAgent run output
New tests fail on
masterand pass with the fix:Full suite (pure Python):
The single failure is
tests/test_connector.py::test_tcp_connector_resolve_host, whichfails identically on
masterin this sandbox (no DNS).black,isort,flake8andmypyare clean on the changed files.Not verified with the C extensions:
pip install -e .fails to link in this environment.The change is confined to
client.py/payload.pyand touches no parser or websocket code.Drafted with Claude Code (Opus 5); reviewed by @bdraco.
Quality Report
Changes: 5 files changed, 97 insertions(+), 5 deletions(-)
Code scan: clean
Tests: failed (FAILED)
Branch hygiene: clean
Generated by Kōan