fix(npm): widen node_modules lock staleness margin to avoid false preemption#35806
Merged
bartlomieju merged 1 commit intoJul 7, 2026
Merged
Conversation
…emption The lax file lock guarding node_modules setup treated the lock holder as dead once its poll-file heartbeat went stale for just 200ms (2x the 100ms update interval), letting waiters proceed without the lock. Under parallel installs with native --allow-scripts packages (notably on Windows), a busy but alive holder can easily miss that window, so multiple processes mutate node_modules concurrently and corrupt the tree. Widen the staleness threshold to 5s and pull the timing values into named constants. OS file locks are released automatically on process exit, so the poll check only needs to backstop delayed release and can afford a generous margin. Refs denoland#35804
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
Fixes concurrent
node_modulescorruption when multiple Deno processes run in parallel against the same project (nodeModulesDir: "auto"), reported in #35804.node_modulessetup is guarded byLaxSingleProcessFsFlag(libs/npm_installer/flag.rs), which combines an OS file lock with a poll-file heartbeat so waiters can detect a lock holder that was killed without releasing the lock. A waiter concluded the holder was dead once the heartbeat went stale for just 200ms (2× the 100ms update interval) and then proceeded without the lock.Under parallel installs of native
--allow-scriptspackages (notably on Windows), a busy but perfectly alive holder — spawning compilers/node-gyp, creating junctions, renaming files — can easily miss that 200ms window. Every waiter then falsely concludes it died and mutatesnode_modulesconcurrently, corrupting the tree. This matches the report's fingerprint: users see "Blocking waiting for file lock on node_modules directory" (the lock is working) and corruption at the same time (os error 3junction failures,EBUSYrenames, half-installed native addons, flakyERR_MODULE_NOT_FOUND).Heartbeat starvation isn't the only way to trip a 200ms margin:
PollFile::touch()ignores write errors — so antivirus interference or a transientEBUSYon Windows stalls the poll file's mtime without the holder ever knowing. Two consecutive failed writes were previously enough to cause false preemption.The 5s margin comfortably covers all of these.
Change
POLL_FILE_UPDATE_INTERVAL_MS,POLL_FILE_STALE_THRESHOLD_MS) with a comment explaining why the margin must be generous.OS file locks are released automatically when a process exits, so this poll check only needs to backstop the rare case of delayed release — it can afford a generous margin. The same lock is used by both the local (
.denolayout) and hoisted installers, so both are covered.Notes
flag::lock tests still pass.node_modulesskips mutation/lock contention entirely in child processes (also helps the startup-cost report in skip/background package check when booting with autoinstall & lock: false #35761). Left out of this PR to keep it focused on the correctness fix.Refs #35804