Skip to main content

Module scratch

Module scratch 

Source
Expand description

Owned temp-path registry — makes the (tmp-dir) / (tmp-file) leak unrepresentable. See the module docs for the incident it closes. ScratchRegistry — the interpreter OWNS every temp path it mints.

§The leak this exists to make unrepresentable (measured 2026-07-31)

tmp-dir / tmp-file used to create_dir_all under std::env::temp_dir() and hand the path back as a bare Value::Str. Nothing owned it, so nothing ever removed it. On rio that produced:

ls -d /tmp/tatara-script-* | wc -l   ->  21,608
du -shc /tmp/tatara-script-*         ->  13 GB
oldest 05:02, newest 22:26, uptime 17h19m  =>  ~1,250 dirs/hour, since boot

That box mounts /tmp as a 48 GiB tmpfs on 29 GiB of RAM, so those 13 GB were not disk — they were memory. Combined with ~25 GB of other scratch it filled RAM and all 31.9 GiB of swap (SwapFree: 176 kB), drove PSI memory.full avg60 to 92%, and left the OOM killer as the only reclaim path — it killed comin’s git mid-deploy. A leaked temp dir is not a tidiness problem on a tmpfs host; it is a memory leak that takes the node down.

§Why a registry rather than “remember to delete it”

The old signature made the leak the DEFAULT and correctness opt-in: a script had to remember an explicit delete, on every exit path including error. The registry inverts that. scratch_dir / scratch_file are the only constructors, they always record, and Drop always removes — so “created but never cleaned” has no representation. Correctness is what you get by doing nothing.

§Two failure modes, two mechanisms

Drop covers normal exit, early return, and panic-unwind. It CANNOT cover SIGKILL, an OOM kill, or a power loss — and on the very host that motivated this, OOM kills were happening three times in six hours. So RAII alone would have left a residue that regrows. [sweep_stale] is the reconciler for that path: a bounded, best-effort sweep of our own prefix, old enough that no live process can still hold it. Invariant for the normal case, reconciler for the violent one.

Escape hatch: set TATARA_SCRIPT_KEEP_SCRATCH=1 to retain scratch for debugging. It is deliberately an env var rather than a Lisp argument — keeping is an operator’s debugging choice, not a script’s contract, and a script that could opt into leaking would reopen the class.

Structs§

ScratchRegistry
Owns the temp paths minted during one interpreter run and removes them on drop.

Functions§

is_scratch_path
Path helper for tests + callers that want to reason about our namespace.
sweep_stale
Remove OUR OWN stale scratch left behind by processes that died without running Drop (SIGKILL, OOM kill, power loss).