docs: state why the heap ceiling is not ordered ahead of the share - #886
Open
ppat wants to merge 1 commit into
Open
docs: state why the heap ceiling is not ordered ahead of the share#886ppat wants to merge 1 commit into
ppat wants to merge 1 commit into
Conversation
The two mechanisms that bound this tree - the watchdog's per-role PSS share here, and the launcher heap ceiling in the operator's dotfiles - are not ordered so that a process fails its own allocation first, and two comments here implied they were or could be. Both are corrected against evidence read off the live pod and out of the server bundle on disk; no share, no envelope and no behaviour changes. The rule relating the two is one line, not a table: a heap ceiling bounds V8's old space while PSS also carries the binary, native allocations, external buffers and V8's other spaces, and that remainder is near-constant per role rather than proportional, so peak PSS is about resting PSS plus the ceiling and the ordering needs ceiling <= share - resting. Applied to this envelope no role clears it. Six of the seven have no ceiling to set at any value; tsserver's is reachable at 179 MiB and refused, because the setting is window-scoped and so applies to every window the operator opens, and because a V8 fatal heap error and a SIGKILL reach the same handler in the TypeScript extension - which stops restarting the service after five crashes in five minutes, a rate a ten-minute dwell cannot reach and a binding ceiling reaches immediately. The extension host comment claimed "capped launcher heaps" as one of the trims that would make it fit. There is no such cap available to it: the server registers no heap setting, exposes no CLI flag, and deletes NODE_OPTIONS from the child environment before every fork it performs, so a variable set in env.tf would cap every mise-node process in the pod and not the extension host. remote.extensionKind - loading less - is the only lever, and this template still cannot depend on it having been applied. The tsserver comment claimed the dotfiles cap would make its share bind. It does not, and the honest reading is worse than the one it replaced: unlike the extension host, tsserver fits at rest, so the oversize rule never covers it and the share means "kill a healthy tsserver" on a large enough project. That kill is recoverable and dwell-limited, which is why it is accepted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0197hA8JPwGMX8wufiQiy6oz
3 tasks
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
Comments and DESIGN.md only. No share, no envelope, no budget and no watchdog behaviour changes. Follow-up to #884, paired with
ppat/dotfiles#789.The premise under investigation was that the watchdog's per-role PSS share and the launcher heap ceiling in the operator's dotfiles are in the wrong order — that the ceiling should bind first so a process fails its own allocation and raises a recoverable V8 fatal heap error, leaving the watchdog as backstop. Two comments in this repo implied that ordering was achievable or already intended. It is not, and both are corrected against evidence read off the live pod and out of the VS Code server bundle on disk.
The rule, in one line
A heap ceiling bounds V8's old space. PSS additionally carries the node binary's share of file-backed pages, native allocations, external
ArrayBuffers and V8's new/code/large-object spaces — a remainder that is near-constant per role rather than proportional to the heap. So the two relate by addition, not by a ratio, and one line covers every role instead of a seven-row table of PSS-to-heap factors:Applied to this envelope, no role clears it
serverMainextensionHosttsservertreeHelperfileWatcherlanguageServerextensionHelperSix of seven have no ceiling to set at any value. The seventh is arithmetically reachable and is refused — see
ppat/dotfiles#789, which keeps the ceiling at 768 on purpose.What each comment claimed, and what replaced it
[extensionHost]claimed "capped launcher heaps" as one of the trims that would make it fit. There is no such cap available to it. Established by reading the server bundle rather than assumed: no heap-sizing setting is registered,server-main.js --helpexposes no memory option, noremote.*configuration property exists in the bundle, and this build has noserver-env-setupsupport.NODE_OPTIONSin particular does not work —server-main.jsdeletes it from the child environment immediately before every fork it performs, on the extension-host path and on the shared path covering the file watcher, pty host and agent host. Setting it fromenv.tfwould cap the parent and every unrelated mise-node process in the pod while leaving the extension host at its default: maximum blast radius, zero effect on the target. The only channel that propagates is the parent's ownprocess.execArgv, injectable only inside the versionedcli/servers/Stable-<commit>/directory VS Code replaces wholesale on every server upgrade — and no share of 2048 MiB is above where the role already rests anyway.remote.extensionKind(loading less) is the only lever, and this template still cannot depend on it having been applied.[tsserver]claimed the dotfiles cap would make its share bind. It does not, and the honest reading is worse than the one it replaced: unlike the extension host, tsserver fits at rest, so the oversize rule never covers it, and on a project large enough to hold it above 320 MB for ten minutes the share means "kill a healthy tsserver" rather than "report" one. That kill is recoverable (the extension restarts the service) and the ten-minute dwell rate-limits it well under the five-crashes-in-five-minutes at which the extension gives up permanently — which is why it is accepted rather than designed away.Coupling to dotfiles (unchanged, and now stated)
The watchdog's behaviour with the dotfiles caps absent is exactly what it was before this PR, which is the state it is actually in today: live tsserver processes still run
--max-old-space-size=3072on a session started afterdotfiles#773. Nothing here becomes load-bearing on dotfiles having been applied.Test plan
pre-commit run --all-filesclean (baseline clean atf8cc458, no unrelated findings touched)./script-memory-watchdog-test.sh— 355 passed, 0 failedbash -n script-memory-watchdog.shRef: #884, ppat/dotfiles#789
🤖 Generated with Claude Code
https://claude.ai/code/session_0197hA8JPwGMX8wufiQiy6oz