Skip to content

fix(download): stop the merge pump waiting on a destroyed stream - #8

Closed
chenrui333 wants to merge 1 commit into
review-base/upstream-devfrom
fix/merge-pump-destroyed-sink
Closed

chenrui333 wants to merge 1 commit into
review-base/upstream-devfrom
fix/merge-pump-destroyed-sink

Conversation

@chenrui333

Copy link
Copy Markdown
Owner

Review copy of a proposed upstream change, based on falcondev-oss/github-actions-cache-server@dev (6ae06c2). Upstream accepts pull requests from collaborators only, so this branch is offered through an upstream issue.

The first download of an unmerged entry tees its parts into the client response and the background merge upload (pumpPartsToStreams). The pump only waits for 'drain', but a destroyed stream returns false from write() and never emits 'drain' (nor 'error' when it is destroyed without one), so the pump parks forever when:

  • the merge upload fails. download() destroys the merger stream without an error, so the client's download stalls mid-body until the client gives up;
  • the client leaves while the pump is waiting on the merger. The next write to the destroyed response stream parks the pump, the merge never completes and its lease keeps renewing.

In both cases the current part's backend read stays open (one S3 socket each), so enough of them fill the S3 agent's socket pool and every other request queues behind it.

This PR waits for 'drain' or 'close', skips destroyed sinks, keeps feeding whichever sink is still open, and stops reading once both are gone so streamParts releases the current part. falcondev-oss#282 and falcondev-oss#291 changed the route side; the pump itself was untouched.

Test

tests/download-merge-sink-gone.test.ts covers the three cases against the real Storage:

Case dev this PR
client leaves after the first chunk: merge completes, lease released, every part stream destroyed merge never settles pass
merge upload fails: client still receives the whole entry client never finishes pass
client and merge both gone: the (endless) part stream is destroyed never destroyed pass

All three fail on dev and pass here with both the filesystem and S3 (RustFS) drivers. pnpm test:run (sqlite + filesystem): 24 files, 67 tests pass, 5 skipped as on dev. type-check, eslint and prettier are clean.

Notes

The first download of an unmerged entry tees its parts into the client
response and the background merge upload. A destroyed stream returns false
from write() and never emits 'drain' (nor 'error' when destroyed without
one), so the pump parks forever when:

- the merge upload fails: `download()` destroys the merger stream, and the
  client's download stalls mid-body until the client times out;
- the client hangs up while the pump waits on the merger: the next write to
  the destroyed response stream parks the pump, the merge never completes,
  and its lease keeps renewing.

Either way the current part's backend read stays open (one S3 socket per
occurrence), so enough of them fill the S3 socket pool.

Wait for 'drain' or 'close', skip destroyed sinks, keep feeding the one that
is still open, and stop reading once both are gone so `streamParts` destroys
the current part stream.

The regression tests fail on the previous pump with the filesystem and S3
drivers: the merge never settles after the client leaves, the client never
finishes after the merge upload fails, and the part stream is never
destroyed when both are gone.

Signed-off-by: Rui Chen <rui@chenrui.dev>
@chenrui333

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-24T20:27:49.173654Z 9558a47 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chenrui333

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

Reviewed commit: 9558a47ec5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chenrui333

Copy link
Copy Markdown
Owner Author

Superseded by upstream v9.10.0 (falcondev-oss#296).

@chenrui333 chenrui333 closed this Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant