Repository navigation
fix(gridfs): close remaining upload races and stream append safely - #16
Open
rockyshi1993 wants to merge 4 commits into
Open
rockyshi1993 wants to merge 4 commits into
rockyshi1993 wants to merge 4 commits into
Conversation
Re-check files after beginResumableUpload inserts a session and never delete chunks of a file that became visible in that window. Roll a finalizing session back to pending after GRIDFS_FILE_EXISTS or GRIDFS_INTEGRITY_MISMATCH so public abort works. Align renameFile release-owner handling with upload so a cleanup failure cannot mask the real result. Re-read visibility after a recovery claim and fail closed when verifyVisibleFile cannot confirm a required visible file. Consume Readable appends incrementally (design A): claim first, honor backpressure, buffer only the residual plus the in-flight chunk, and reject trailing partial or oversize input during consumption. Add an inspect-only, paginated orphan reporter with no apply or delete path. Clarify buffering, large-file resumable guidance, and the caller-persisted verification contract in the English and Chinese docs. Record Search/Vector Search Atlas deployments as UNVERIFIED.
The remaining-issue change set exceeded the 890000 / 887000 ceilings (892639 CJS / 889922 ESM in the local 2026-11-12 build). The growth is from the incremental Readable append consumer, the beginResumableUpload post-insert files re-check, finalize pending rollback, and rename release-owner alignment; dependencies are unchanged. Raise to 896000 / 893000 to keep about 3 KiB of reviewed headroom.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 7f8bf89. Configure here.
…e docs R1-01: consumeReadableChunks now handles already-destroyed streams and premature close (destroy without error). The read promise rejects in finite time, settles once, waits for an in-flight onChunk write before settling, and removes its listeners on every path so activeAppend can be released. R1-03: gridfs-inspect-orphans streams NDJSON findings, caps each run with maxFindings (default 1000), and exposes an exclusive resumeAfter.filesId cursor. Summary counters accumulate incrementally. The tool remains inspect-only. R1-04: English and Chinese GridFS docs no longer say a failed append returns nextChunk. Clients resume from the last successfully acknowledged position, or from GRIDFS_RESUME_MISMATCH.expectedNextChunk when the stored prefix has moved.
After the R1 documentation corrections, the English search index measures 1800511 bytes, 511 over the 1800000 ceiling. CI run 43196147300 already failed at 1800085 on the previous PR head; this round added the failed append resume contract (R1-04) and inspect memory-bound run notes (R1-03) on the GridFS and recovery pages. The Chinese index is 1672743 and the combined total 3473254 remains under 3500000. The growth is from GridFS remaining-issue documentation, not from new website dependencies. The maintainer explicitly authorized raising this ceiling instead of trimming documentation. Raise maximumSearchIndexBytesPerLocale from 1800000 to 1804000 to keep about 3.4 KiB of reviewed headroom.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Single PR for the remaining GridFS issues, based on current
main(693f214, which already includes merged MZ-17 / PR #15). PR #15 is left untouched.693f21410c1c5db166cc1d836f9863e4b6c674e77f8bf89cb9d5af0b8d50d0d4d42e79661ad5f9cf43850aa(functional + tests + docs),7f8bf89(package-budget raise only, explained below)File changes by item
1. P1
beginResumableUploadprecheck racesrc/capabilities/gridfs/resumable.ts— after the session insert, re-checkfiles. If a file is visible, drop the new pending session (best effort) and throwGRIDFS_FILE_EXISTS. NeverdeleteManychunks of a visible file.test/unit/gridfs/gridfs.test.ts,test/integration/gridfs/gridfs-remaining-issues.test.ts2. P2 finalize stuck in
finalizingsrc/capabilities/gridfs/resumable.ts— after this worker's pending→finalizing CAS,GRIDFS_FILE_EXISTS/GRIDFS_INTEGRITY_MISMATCHfromcommittedFileor a post-insert duplicate conflict CAS-restore the session topending. Already-finalizingretry path and ambiguous driver errors still holdfinalizing.gridfs-remaining-issues.test.ts(abort via public API works; visible chunks are retained).3. P2
renameFilefinallymasking the real resultsrc/capabilities/gridfs/operations.ts— aligned withupload/uploadStream: warn on release failure, return the successful rename or rethrow the original error.test/unit/gridfs/gridfs.test.ts4. P2
gridfs-recover.cjsstale pre-claim visibility / uncheckedverifyVisibleFilescripts/maintenance/gridfs-recover.cjs— apply re-readsfilesafter the exclusive claim; visibility changes fail closed. If a required visible file cannot be verified (verifyVisibleFilereturns null), fail closed and leaverecovering.test/integration/gridfs/gridfs-stale-recovery.test.tsdocs/en/gridfs-recovery.md,docs/zh/gridfs-recovery.md5. MZ-02 design A — incremental
appendChunks(Readable)src/capabilities/gridfs/stream-chunks.ts(not a public export)src/capabilities/gridfs/resumable.ts— CAS-claimactiveAppendfirst;Buffer/Uint8Arraystill plan-then-write;Readablewrites complete chunks as they arrive; residual + currentread()buffer only; trailing partial / oversize rejected during consumption; replay / busy / no deletion of others' chunks unchanged.test/unit/gridfs/stream-chunks.test.ts(peak memory does not scale with input size, backpressure, partial/oversize, onChunk error), plus unit and integration append coverage.6. MZ-06 docs-only
docs/en/gridfs.md,docs/zh/gridfs.md— keep “no visible wrong file before verification”; disclose full buffering ofupload(Readable, { sha256 }); large files should use resumable + a modest-highWaterMarkReadable. No new API.7. MZ-07 docs-only
uploadId/length/sha256. After a vanished session,getFile+ re-hash actual chunks; never trustmetadata.sha256alone. No new public API.8. MZ-03 inspect-only operator script
scripts/maintenance/gridfs-inspect-orphans.cjs— paginated walk of{ files_id, n }, unified files+uploads re-check, report only (visible_file/owned/candidate_orphan). No apply, no delete, no age-based deletion, not on the public bucket API.test/integration/gridfs/gridfs-inspect-orphans.test.ts9. MZ-09 / 13 / 14 — docs only
docs/en/support-matrix.md,docs/zh/support-matrix.md— GridFS memory-server coverage is 7.0.37 and 8.0.26; Search / Vector Search against Atlas, atlas-local, or mongot remain UNVERIFIED.Test runner wiring
test/run-tests.cjs,scripts/validation/run-memory-server-matrix.cjs— new suites added (gates strengthened, not weakened).Package budgets (separate commit)
config/package-budget.json— ceilings raised 890000→896000 CJS and 887000→893000 ESM. Local build after the functional commit was 892639 / 889922, which exceeded the previous ceilings. No dependency change. Headroom ~3 KiB.Design decisions
finalizingin-flight owner stay fenced.maxAppendBytesor any new public option. Peak-memory instrumentation is an internal callback on the unexported helper.Test commands, versions, and results
Local box: Node v24.22.1,
mongodb-memory-server10.4.3. CI Node 18/20/22 was not waited on and Node 20 was not run locally (UNVERIFIED here).P1 evidence (deterministic hook; fail on main, pass on branch)
Script: evidence checkout path
beginResumableUpload-precheck-race-deterministic.cjs(MongoDB 7.0.37, Driver 6.21.0).mainworktree693f214passed: false,errorCode: null,fileVisible: true,chunkCount: 0,downloadedLength: 0,corruption: true(visible file, chunks deleted,download()returns empty Buffer)7f8bf89passed: true,errorCode: "GRIDFS_FILE_EXISTS",chunkCount: 1,downloadedLength: 4,sessionStatus: null,corruption: falseGates
npm run lintnpm run type-check(build +tsc --noEmit+ tsd includinggridfs-usage)npm run buildnpm run check:docs-examplesnpm run check:doc-claimsnpm run check:package-budgetsGridFS suites (new tests included), 73/73 pass in each of four combinations
Commands (serial):
npm install mongodb@7.8.0 --no-save)Driver restored to 6.21.0 afterwards.
package-lock.jsonwas not committed.Risks
appendChunkscan persist a complete prefix and then reject a trailing partial; callers must retry from the reportednextChunk. Documented.Bufferpayloads are still fully planned before any insert (existing tests unchanged).{ _id, status: 'finalizing' }(same CAS shape as the existing validation rollback). Only the pending→finalizing winner reaches this path.candidate_orphanas a delete authorization.CI is not being waited on; run links will be recorded once triggered.
Note
High Risk
Touches GridFS ownership, chunk lifecycle, and crash-recovery paths where mistakes can corrupt or delete user data; streaming append can leave partial chunks before failing.
Overview
This PR hardens GridFS resumable upload and operator tooling after several race and stuck-state bugs.
Resumable upload:
beginResumableUploadnow re-checksfilesafter the session insert and fails closed withGRIDFS_FILE_EXISTSwithout deleting chunks of a concurrently visible file.appendChunksgains incrementalReadableconsumption via internalstream-chunks.ts(bounded memory, backpressure, partial/trailing reject with retriable prefix).finalizeResumableUploadrollsfinalizingback topendingon definitiveGRIDFS_FILE_EXISTS/GRIDFS_INTEGRITY_MISMATCHsoabortResumableUploadstill works.renameno longer masks the real outcome when reservation release fails.Operations:
gridfs-recover.cjsapply mode re-reads file visibility after the exclusive claim and fails closed if visibility changed orverifyVisibleFilecannot confirm a required visible file. New inspect-onlygridfs-inspect-orphans.cjspaginates chunks and classifiesvisible_file/owned/candidate_orphanwithout delete or apply.Docs (en/zh) and support-matrix notes align with streaming semantics and verification expectations. Package bundle ceilings rise to 896000/893000; new unit/integration suites are wired into test runners.
Reviewed by Cursor Bugbot for commit af16030. Bugbot is set up for automated code reviews on this repo. Configure here.