test(repro): minimal Workers reproducer for the streamed-PUT failures - #203
test(repro): minimal Workers reproducer for the streamed-PUT failures#203alukach wants to merge 1 commit into
Conversation
Isolates the write path with no S3, no auth and no registry, leaving one variable: whether the inbound body stream is touched before or after an await. The post-await arm mirrors WorkerBackend::forward for PutObject/UploadPart; the pre-await arm mirrors the op_needs_buffered_body guard. Reproduces the 503 branch (1 failure in 32 post-await, 0 in 32 pre-await, "Network connection lost" -> BackendError -> 503). Intermittent: a later 96/96 run was clean, so a green run is inconclusive. Does not reproduce the majority 520 branch. Prod evidence now shows that one is relayed from the backend fetch rather than the edge killing the worker -- Forward passes the upstream status through verbatim and the worker's own 5xx log records status=520 with handle_request returning normally. See the README. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @alukach's task in 1m 6s —— View job Reviewing PR #203
✅ No blocking issues — safe to merge. This is a self-contained, non-production reproducer (isolated under A couple of very minor, non-blocking observations:
Nothing here rises to a correctness bug or security issue — the harness only talks to |
|
🚀 Latest commit deployed to https://source-data-proxy-pr-203.source-coop.workers.dev
|
Standalone reproducer under
tests/repro/streamed-put-io-context/. Not wired into CI — it is a manual tool and an artifact to hand upstream.What it isolates
No S3, no auth, no registry. One variable: whether the inbound body stream is touched before or after an await.
/before— attach the stream in the request's own I/O context. MirrorsProxyGateway::op_needs_buffered_body, the guard that covers the multipart control ops and batch delete./after— await first (standing in for the registry lookup and STS exchange), then attach. MirrorsWorkerBackend::forwardforPutObject/UploadPart, which the classifier explicitly excludes.forward()copiesWorkerBackend::forwardfaithfully, including theFixedLengthStreamwrapper and its droppedpipe_topromise.Result
1 failure in 32 on
/after, 0 in 32 on/before, at concurrency 8 × 16 MiB:Intermittent — a later 96/96 run was clean. A green run is inconclusive.
Scope
Network connection lostmaps toBackendError→ 503, the minority prod mode (2 of 23 in one sample). It does not reproduce the majority 520.Prod evidence now shows the 520 is relayed from the backend fetch:
GatewayResponse::Forwardpasses the upstream status through verbatim, and the worker's own 5xx log (from #201) recordsstatus=520withhandle_requestreturning normally. tessera's backend is plain S3 in us-west-2, which does not emit 520 — so the runtime is synthesizing it for a subrequest the worker issued. That remains open.The accompanying
TypeError: Can't read from request stream after responding with an exceptionfires ~1 ms after the response commits — the orphanedpipe_tostill reading the inbound body. A consequence rather than the cause, but it is what resets the connection so the client never sees the relayed status.🤖 Generated with Claude Code