← board

testmgr: cap its own memory in a cgroup scope + swap-aware admission

Symptom

An --tier opt run (optdiff shards, scratch /tmp/testmgr-scratch-1821920) drove the machine into swap thrash. Journal died mid-write:

Jul 12 17:08:30 borg systemd-resolved[1030]: Under memory pressure, flushing caches.
Jul 12 17:08:33 borg systemd-journald[334]: Under memory pressure, flushing caches.
Jul 12 17:08:55 borg systemd-journald[334]: Under memory pressure, flushing caches.
   <log ends — box unresponsive, hard reset>

Nothing was OOM-killed. 25 seconds from first pressure to a dead desktop.

Why nothing saved us

Secondary: jobs with no learned RSS are charged est_mem = 32 MB (tools/testmgr.py:508). Self-compile and optdiff shards are hundreds of MB, so a cold metrics store systematically under-charges exactly the fat jobs.

Fix

1. Self-scope (the real fix — makes a freeze structurally impossible). testmgr re-execs itself into a memory-capped cgroup at startup, so a runaway job is killed by the kernel inside the scope and the desktop never stalls. No caller-side setup, works for make targets, cron and the twatch daemon alike:

# early in main(), before any job is scheduled
if os.environ.get("TESTMGR_SCOPED") != "1" and shutil.which("systemd-run"):
    os.environ["TESTMGR_SCOPED"] = "1"
    os.execvp("systemd-run", [
        "systemd-run", "--user", "--scope", "--quiet",
        "-p", "MemoryMax=8G", "-p", "MemorySwapMax=1G",
        sys.executable, *sys.argv])

Cap should be derived from box RAM (e.g. min(8G, 60% of MemTotal)), not hardcoded. Degrade gracefully when systemd-run is absent or the user has no systemd session (CI containers): skip the re-exec, keep going.

2. Swap-aware admission. admit_ok must also refuse when free swap is low or memory PSI is already climbing — MemAvailable alone is a broken signal on a swapping box:

3. Honest cold-start estimate. Raise the unlearned-job est_mem default, or seed it per job class (CLASSES), so a cold metrics store doesn't admit a swarm of self-compile shards at 32 MB apiece.

Acceptance

Notes

Out of scope, host-side (user did these on 2026-07-12): vm.swappiness=10 (60 pushed 2.3 GB of idle anon into swap while 11 GB RAM sat free — that swapped anon is the refault storm) and installing earlyoom as a system-wide backstop. Neither belongs in the repo; testmgr must not depend on them.

Log