fix(fetch): flush partial progress when a download connection drops - #1388
Conversation
|
Warning Review limit reachedNext included review available in 15 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Swept the workspace for the same shape — a buffered writer whose flush sits after a loop that can return early — to check whether this was one instance or a pattern:
|
|
Stating the strength of that honestly: the failure was intermittent (main's macOS check was green throughout), so one green run is consistent with the fix rather than proof of it. The load-bearing argument is the mechanism — unflushed buffer → short |
|
Updating the evidence, since it is now considerably stronger than when I opened this. Without this fix — four macOS failures, all identical (
None of those four PRs touch With this fix: I opened this describing the failure as intermittent, on the strength of main's macOS check being green. Four-for-four on branches against one green on main does not read as intermittent any more — it reads as reliably reproducible on macOS runners, with main's single green being the outlier worth explaining rather than the baseline. That also sharpens why loosening the assertion to |
|
Correction to my previous comment: I said the failure "does not read as intermittent any more." That overstated it. Checking properly, main is 8 consecutive green macOS runs ( The accurate picture is an asymmetry, not determinism:
Same code both sides. The plausible difference is machine load — a PR run schedules ~94 checks including the full board-build matrix, main's runs are lighter — and this is a race between a buffered write reaching the file and the connection error surfacing. Heavier load widens the window. That is still a real bug with a real fix; it just makes the failure load-dependent rather than platform-deterministic, and it explains main's clean record without needing main to be an outlier. It also means one green macOS run here remains supporting evidence rather than proof — the mechanism is still the load-bearing argument. |
#1370's resume path could lose the bytes it had already received, on the one path where keeping them is the entire point. `fetch_into_part` writes chunks to a `tokio::fs::File` and flushes after the read loop ends — but a dropped connection returned from *inside* the loop, skipping the flush. `tokio::fs::File` makes no promise to flush on drop, so the delivered bytes could go nowhere. The retry loop then stats a short `.part`, computes a stale `resume_from`, and asks the server for a range it already holds. The download still completes, so nothing looks wrong — it just costs a full extra round trip and re-transfers bytes the feature was written to avoid re-transferring. The body error is now recorded and returned after the flush rather than instead of it. This surfaced as `streaming_download_resumes_from_the_byte_offset_after_a_drop` failing on macOS runners with `left: 3, right: 2`. The exact count is the only assertion that can detect this: the resumed file is byte-identical either way, so every other check in that test passes while the resume quietly does nothing. Comment added at the assertion saying so, because loosening it to `>= 2` is the obvious-looking fix and it buries the bug. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
7becc31 to
8d00e88
Compare
|
Post-merge confirmation, which is stronger evidence than anything available while this was open. #1387 (ratchet batch 3) failed That is the control I was missing earlier: my own green run here could have been the timing window closing by luck, but an unrelated PR flipping from reliably-red to green across exactly this merge is what the mechanism predicts. Recap of the record, for anyone who finds this later:
|
#1370's resume path could lose the bytes it had already received — on the one
path where keeping them is the entire point.
The bug
fetch_into_partwrites chunks to atokio::fs::Fileand flushes after theread loop:
The comment is right about why the flush matters. It just wasn't reachable
from the case that matters: a dropped connection returns from inside the
loop, skipping it.
tokio::fs::Filemakes no promise to flush on drop, so thedelivered bytes could go nowhere.
The retry loop then does:
…reads a short
.part, computes a stale offset, and asks the server for arange it already holds. The download still completes — it just costs a full
extra round trip and re-transfers bytes the feature exists to avoid
re-transferring.
The body error is now recorded and returned after the flush rather than
instead of it.
How it surfaced, and the wrong fix I nearly shipped
streaming_download_resumes_from_the_byte_offset_after_a_dropstarted failingon macOS runners:
My first read was "the connection count is a reqwest pooling detail, loosen it
to
>= 2". I had that committed. It is wrong, and worth writing down why:the exact count is the only assertion that can detect this bug. The resumed
file is byte-identical whether the resume works or restarts from zero, so the
byte-identity check, the
.part-cleanup check and the progress-monotonicitycheck all pass while the feature silently does nothing. A third request is
the symptom.
I've added a comment at the assertion saying exactly that, since
>= 2is theobvious-looking fix and it buries the defect.
Why macOS
Buffered-write timing. Nothing about the bug is macOS-specific — Linux and
Windows runners happened to get the bytes to the file descriptor before the
error surfaced. main's macOS check is currently green, so this is intermittent
rather than deterministic; it reproduced on two unrelated branches
(#1384, #1386) within an hour.
Verification
soldr cargo test -p fbuild-packages-fetch --lib— 138 passed, 0 failed, withthe strict
== 2assertion in place. clippy-D warningsclean.