Skip to content

feat(bench): repeatable memory-footprint benchmark (mem-bench.mjs + advisory workflow) - #406

Merged
debugmcpdev merged 3 commits into
mainfrom
feat/mem-bench
Aug 21, 2026
Merged

feat(bench): repeatable memory-footprint benchmark (mem-bench.mjs + advisory workflow)#406
debugmcpdev merged 3 commits into
mainfrom
feat/mem-bench

Conversation

@debugmcpdev

Copy link
Copy Markdown
Collaborator

Description

PR #395 showed that a memory claim about this server ("startup /proc scan leaves a permanent ~200 MB RSS scar") was measurable, repeatable, and fixable — but we had no tooling to make such claims cheap to verify or to catch regressions. This adds a repeatable memory-footprint benchmark so footprint decisions are driven by data instead of chance.

scripts/mem-bench.mjs (zero new dependencies — drives the server with the already-vendored MCP SDK client):

  • Checkpoints in one server lifecycle, one fresh process per trial: after-initialize (startup floor — a successful MCP initialize is a guaranteed barrier for reaper completion, since both reapers are awaited before argv parsing), after-tools-list (prices the adapter availability probe, perf(adapters): first tools/list imports and instantiates all 9 adapter packages permanently #401), after-session-cycle + per-session RSS delta (retention signal).
  • busy-proc scenario (docker): spawns N sleep infinity processes inside the container before exec'ing the server, so exactly N sleepers exist in /proc when the reapers run and node is PID 1 — a controlled, repeatable stand-in for a busy host, no --pid=host nondeterminism. Each trial runs a 0-sleeper control and an N-sleeper variant; the within-trial delta isolates scan cost. procEntryCount is recorded per trial so a failed spawn is visible, not silently wrong.
  • Sampling: /proc/<pid>/status VmRSS + VmHWM on Linux (peak proves a spike was prevented, not released — V8 doesn't return grown arenas), Get-Process WorkingSet64/PeakWorkingSet64/PrivateMemorySize64 on Windows, ps on darwin, docker exec cat /proc/1/status for containers. Settle = 3 consecutive samples within max(2%, 2 MB).
  • Statistics: median + min..max over --trials (default 5); JSON output with git/host/config provenance; compare a.json b.json subcommand marks a delta significant only when the median shift exceeds both runs' full spreads.
  • npm scripts bench:mem / bench:mem:docker; bench-results/ gitignored.

.github/workflows/mem-bench.yml: advisory only — weekly cron + workflow_dispatch, ubuntu-only, results to step summary + uploaded JSON artifact. Never a required check, no thresholds, no PR trigger (shared-runner memory numbers are noisy; the local harness is the source of truth).

Baselines at this commit

Windows host, dist target, 5 trials (all settled):

checkpoint RSS MB (median) min..max notes
after-initialize 107.8 107.4..108.9 startup floor, stdio
after-tools-list 112.6 111.9..113.5 +4.8 MB = adapter availability probe (#401)
after-session-cycle (10× mock) 88.8 88.4..89.3 peak 119 MB during churn; ~201 KB/session drift

Docker (linux), busy-proc, 5 trials — the #395 verification this harness was first used for:

variant base a4d5737d PR #395 Δ
quiet (4 procs) 98.2 MB 97.9 MB within noise
busy (1504 procs) 136.3 MB 106.8 MB −29.5 MB (−21.6%), VmHWM drops identically

🔄 Type of Change

  • ✨ New feature (tooling)

🧪 Testing

  • pnpm lint clean (script is covered by scripts/** lint glob)
  • Smoke-verified on Windows host (dist target, all scenarios) and docker target (quiet/busy variants, sleeper spawn verified via procEntryCount)
  • Used in anger for the perf(reaper): bound /proc scan concurrency at startup #395 verification above

Related: #399 #400 #401 #402 #403 #404 #405 (the footprint follow-ups this data supports)

🤖 Generated with Claude Code

cynarlab and others added 3 commits August 21, 2026 19:15
….mjs)

Measures server RSS at lifecycle checkpoints (after-initialize = reaper
barrier, after-tools-list, session-cycle retention) across targets
(dist / npx bundle / docker), with a synthesized busy-/proc docker
scenario (N sleeper processes spawned before exec, server as PID 1) to
exercise the startup orphan reapers under a realistic process count.
5-trial median + min..max sampling, JSON output, and a compare
subcommand whose significance test is median-shift vs full spread.
Advisory weekly/dispatch CI workflow (never a required check).

Motivated by PR #395's finding that unbounded /proc scans leave a
permanent RSS scar; this makes such claims measurable and repeatable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ckout

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@debugmcpdev
debugmcpdev merged commit 9ba7c9d into main Aug 21, 2026
9 checks passed
@debugmcpdev
debugmcpdev deleted the feat/mem-bench branch August 21, 2026 23:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants