feat(page-cache): reclaim stray /tmp mission trees to cut billed Railway footprint#2454
Conversation
…print The universal page-cache reclaim (#2374) only drops clean pages under its roots (instance/ + venv + scratch + project workdirs). Mission subprocesses read large files into the page cache from stray /tmp trees (pytest-of-*, test-koan*, koan-*, jest_rs) the roots never cover, so those pages stay billed on the cgroup's memory.current until the age-gated post-mission sweep deletes the files — hours later. Observed live 2026-07-19 on a fresh idle instance: anon 78 MB / RSS ~124 MB (healthy), but billed memory.current ~600-900 MB, with a mission tree pinning ~570 MB of out-of-root file cache for ~4.5h (the "stable at 1 GB for hours" operators see). No leak — pure cgroup page-cache accounting. - default_reclaim_roots() now also sweeps the same stray /tmp trees the tmp sweep targets (cleanup.extra_tmp_globs), own-uid dirs only (read-only fadvise; skip other users' trees to avoid permission-denied churn). - Lower page_cache_reclaim.idle_interval_s default 900 -> 180 so boot and between-tick cache never sits billed for long. Architectural: extends the default_reclaim_roots() contract in specs/components/agent-loop.md (declared below). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@sukria-koan0 fix failing CI tests |
CI's pytest basetemp lives under /tmp/pytest-of-runner, so the prior test's tmp_path pattern started with /tmp/ and passed the guard, returning the dir instead of []. Use a fixed non-/tmp pattern and spy on glob to prove the guard short-circuits before globbing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@sukria-koan0 review (CI is now full green) |
Resolved. Failure came from pytest rooting its tmpdir under |
PR Review — feat(page-cache): reclaim stray /tmp mission trees to cut billed Railway footprintSolid, well-scoped billing-hygiene fix. Merge-ready. Strengths:
Checklist
Automated review by Kōan (Claude · model opus) |
sukria-koan0
left a comment
There was a problem hiding this comment.
Tip
No blocking issues found — ready to merge.
Summary
Since v0.8.0 the Railway memory graph plateaus near 1 GB while real process memory is tiny. Root cause is not a leak — it is cgroup
memory.currentpage-cache accounting.Diagnosed live on a fresh idle instance (2026-07-19):
anon(real private RAM)ps)memory.currentThe reclaim log showed
filedropping either to ~95 MB (fully reclaimed) or plateauing at ~663 MB — a single mission/tmptree pinning ~570 MB of out-of root page cache that held for ~4.5h until the age-gated post-mission sweep deleted the files. That is the "stable at 1 GB for hours" an operator observes.The universal reclaim (#2374) only fadvises pages under its roots (
instance/+ venv + scratch + project workdirs); the stray/tmpmission trees (pytest-of-*,test-koan*,koan-*,jest_rs) were never covered.Changes
default_reclaim_roots()also sweeps the stray/tmpmission trees matchedby
cleanup.extra_tmp_globs— own-uid dirs only (read-onlyfadvise; skipother users'
/tmpto avoid permission-denied churn). Their clean pages are nowdropped immediately, decoupling billed footprint from the sweep's deletion latency.
page_cache_reclaim.idle_interval_sdefault 900 → 180s so boot andbetween-tick page cache never sits billed for long.
specs/components/agent-loop.md),instance.example/config.yaml,and
docs/operations/memory-footprint.mdupdated; new test coverage for theglob / dir-only / own-uid filters.
The ultimate backstop for any out-of-root cache (and for kernel
slab, whichfadvisecannot touch) remains a cgroup memory limit on the service — noted in the docs.Architectural declaration
This extends the durable
default_reclaim_roots()contract inspecs/components/agent-loop.md— changed contract-first, then the code made to conform.Checklist
(
specs/components/agent-loop.md), declared above and reviewed contract-first.test_page_cache.py,test_config.py,test_run_finally_reclaim.py)make lintclean🤖 Generated with Claude Code