fix(fs): fsPromises.watch() delivers post-pull writes (#5099 regression)#5117
Conversation
… post-pull writes are delivered (#5099 regression) #5099 seeded the promises-watcher snapshot at watch() creation time AND stopped re-snapshotting at the first .next() pull (in start_promise_watcher), intending to deliver writes emitted between watch() and the first iteration. That broke post-pull delivery. With the baseline frozen at the empty creation-time state, the first poll after the first .next() diffs that empty baseline against the post-early-write directory and resolves the *first* pending pull with the pre-iteration write. The promises-lazy-start test reuses that same pending promise for the next race, so the later write (late.txt) is never delivered to it — and the pre-iteration write is wrongly surfaced. Result: both "pre-pull ignored" and "post-pull delivered" assertions fail against Node. Fix: re-take the baseline snapshot in start_promise_watcher at the first .next() (folding the current directory state into the baseline, so pre-iteration writes are ignored — matching Node, whose async iterator only starts collecting once you iterate), then let promise_watcher_poll_impl keep advancing the baseline after every poll (so a write *after* a pull is detected as a fresh change and delivered). The creation-time snapshot_watch_target call is kept solely as the synchronous path validation / error path (Node throws ENOENT etc. at watch() call time). Validation (node v26): - fs/watch/promises-lazy-start.ts: deterministically prints "pre-pull ignored: true" / "post-pull delivered: true" (3/3). - fs/watch/promises-watch.ts: still PASS. - Isolated repro: a write after a pull begins is delivered to that pull.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthrough
ChangesSnapshot re-baselining on first async iterator pull
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Problem
PR #5099 changed
fsPromises.watch()to seed its snapshot baseline at creation time and to keep that creation-time baseline at the first.next()pull (removing the re-snapshot instart_promise_watcher). The stated intent was to deliver events emitted betweenwatch()and the first.next().That broke post-pull delivery. With the baseline frozen at the empty creation-time state, the first poll after the first
.next()diffs that empty baseline against the post-early-write directory and resolves the first pending pull with the pre-iteration write.fs/watch/promises-lazy-start.tsreuses that same pending promise for the next race, so the later write (late.txt) is never delivered to it — and the pre-iteration write is wrongly surfaced. Bothpre-pull ignoredandpost-pull deliveredassertions then fail against Node.Root cause
start_promise_watcher(incrates/perry-runtime/src/fs/dir_glob_watch.rs) no longer refreshedstate.snapshotat the first pull, so the per-poll diff started from stale creation-time state. The per-poll advance (state.snapshot = current) was fine on its own, but the initial baseline was wrong.Fix
Re-take the baseline snapshot in
start_promise_watcherat the first.next()(folding the current directory state into the baseline so pre-iteration writes are ignored — matching Node, whose async iterator only starts collecting once you iterate), then letpromise_watcher_poll_implkeep advancing the baseline after every poll (so a write after a pull begins is detected as a fresh change and delivered).The creation-time
snapshot_watch_targetcall is kept solely as the synchronous path-validation / error path (Node throwsENOENTetc. atwatch()call time). #5099 is not reverted wholesale.Validation (node v26)
fs/watch/promises-lazy-start.ts: deterministically printspre-pull ignored: true/post-pull delivered: true(3/3).fs/watch/promises-watch.ts: still PASS.cargo test -p perry-runtime -p perry-stdlib: no failures attributable to this change (touches onlyfs/dir_glob_watch.rs).Summary by CodeRabbit
Bug Fixes
Documentation