Repository navigation
fix(market): bound generation installs by progress, not one total duration - #860
Open
Yuncan050115 wants to merge 1 commit into
Open
Yuncan050115 wants to merge 1 commit into
Yuncan050115 wants to merge 1 commit into
Conversation
…ation defaultRunInstall armed a single 90-second timer at spawn and SIGKILLed whatever it caught. pnpm streams Progress lines continuously while it downloads and unpacks, so a large platform package (dsh-ops@0.4.4 pulls a 392 MB Git-for-Windows payload; the reporter measured 73.5s of pnpm alone on a cold store) crossed the line mid-work, exited null->1, and the log recorded only the last Progress: line as if pnpm had failed. Cold-cache users could not install such plugins at all until their store was warm. Idle now means idle: every chunk re-arms the 90-second clock, and a separate per-phase hard ceiling still ends a process that keeps talking but never finishes. The ceiling is six minutes so both phases plus staging stay inside the registry lock's own budget (waiters give up at 15 minutes, a lock is judged stale at 20, with no heartbeat in between). Both bounds are overridable (installTimeoutMs keeps its name with the new meaning; installHardTimeoutMs is new) and only safe positive integers count, mirroring the minimumReleaseAge guard: a 0 or NaN override would otherwise SIGKILL every install on the next tick. A kill that never reports close settles after pnpm-runner's five-second grace instead of hanging the promise. Every timeout names itself in the trace and output, so a kill no longer masquerades as a pnpm error in generation.log. Adds test/generation-install-timeout.test.ts driving the real defaultRunInstall through a scripted pnpm-like child, with chunk intervals an order of magnitude below each idle bound: continuous progress past the old total-duration point completes, a stalled child dies on the idle bound, a churning child dies on the hard ceiling, and invalid override ceilings fall back to the defaults. All fail against the previous single-clock implementation. Refs dataelement#851
Yuncan050115
force-pushed
the
fix/generation-install-idle-timeout
branch
from
November 6, 2026 04:49
5f338ec to
48e818e
Compare
Author
|
Adversarial review folded in (48e818e):
Wording: the notice contains the substring "error", so if pnpm emitted an |
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.
Refs #851.
What
defaultRunInstallarmed one timer at spawn and SIGKILLed the pnpm child when it fired:installTimeoutMshas no caller anywhere in the repo, so 90 seconds is a total-duration hard limit on every generation install. pnpm streamsProgress:lines continuously while downloading, so a plugin carrying a large platform payload —dsh-ops@0.4.4pulls@dsh-ops/bash-win32-x64, 392 MB unpacked — gets killed mid-download on a cold store,code ?? 1records it as exit 1, anddiagnosticLine()has nothing but the lastProgress:line to report. The reporter measured pnpm itself finishing in 73.5s on a cold store; the market path adds staging, hoisting and peer validation on top, which lands past 90s deterministically.Change
The timeout measures idleness again rather than duration. Every stdout/stderr chunk re-arms the 90-second bound, so only a process that stopped talking dies on it. A separate hard ceiling — six minutes per phase — still ends a process that keeps producing output but never finishes; six minutes is chosen so both phases plus staging stay inside the registry lock's own budget (waiters give up at 15 minutes, a lock is judged stale at 20, no heartbeat in between; pnpm-runner already treats that fifteen-minute ceiling as the host contract). Both bounds are overridable and accept only safe positive integers, same guard style as
minimumReleaseAge— a 0 or NaN override would otherwise SIGKILL every install on the next tick. A kill whoseclosenever arrives settles after pnpm-runner's five-second grace instead of hanging the promise. Every timeout names itself in the trace and the output (generation install timed out and the pnpm process was killed (…)), so a kill no longer masquerades as a pnpm error ingeneration.log.Test plan
test/generation-install-timeout.test.tsdrives the realdefaultRunInstallthrough a scripted pnpm-like child: continuous progress past the old kill point completes and a generation lands (chunk interval is idle/10 so slow CI cannot starve the re-arm); a silent child dies on the idle bound with the timeout named indetailand the trace; a churning child dies at the hard ceiling;0 / -5 / NaN / 12.5overrides fall back to the defaults. All four shapes verified red onorigin/main(the first fails withinstall was killed, the others produce no timeout diagnostic), green with the fix.vitest run test/generation-install-timeout.test.ts test/generation-boundary.test.js test/market-installer.test.js test/generation-installer.test.ts: 78 passed / 1 failed — the failure (accepts named Harness fallback links…) reproduces identically on a cleanorigin/maincheckout in this environment.tsc --noEmit -p tsconfig.node.json: exit 0.git diff --checkclean.Not changed deliberately: progress stays "any byte on stdout/stderr"; the six-minute ceiling is a chosen default aligned to the lock budget, not measured per package.