3.6 KiB
3.6 KiB
name, description
| name | description |
|---|---|
| detached-fleet-job-launch | Launch long-running jobs (fleet hosts or local workstation) so they survive ssh/logind/harness teardown; includes worker takeover law for queue daemons |
Detached long-running jobs on Dank fleet hosts
Law
ssh host 'setsid nohup CMD & ' is NOT detached. systemd-logind deactivates
session-N.scope when the ssh command returns and SIGKILLs the whole cgroup:
no journal entry, no OOM trace, python block-buffers stdout so the log stays
0 bytes. setsid/nohup do not escape the cgroup.
Canonical launch (remote)
ssh host 'export XDG_RUNTIME_DIR=/run/user/$(id -u); \
systemd-run --user --collect --unit=NAME \
-p SendSIGKILL=no \
-p StandardOutput=append:/home/dank/logs/NAME.log \
-p StandardError=append:/home/dank/logs/NAME.log \
sh -c "CMD -u; echo \$? > /home/dank/logs/NAME.rc"'
- transient user unit = outside every session scope; survives ssh teardown
(requires
Linger=yes, true fordankon both hosts) echo $? > …rcinside the unit = completion sentinel the unit itself writes-u/ unbuffered output so the log is live- verify launch in the same ssh call:
systemctl --user is-active NAME.service
Local workstation variant (verified 2026-10-10, blankpad)
Harness background jobs get tree-killed at timeout — setsid/nohup children die too (survives the process-group kill, not the tree walk). Same cure as remote, run locally:
loginctl enable-linger $(whoami) # default Linger=no kills user units at session end
XDG_RUNTIME_DIR=/run/user/$(id -u) systemd-run --user --collect --unit=NAME \
-p StandardOutput=append:/abs/path/job.log \
-p StandardError=append:/abs/path/job.log \
/full/path/to/binary args...
systemctl --user is-active NAME.service
Gotchas:
- bash-tool env lacks
XDG_RUNTIME_DIR→ set it inline. --collectGCs the unit when it exits — after a crash-exit,systemctl --user restart NAMEfails "Unit not found"; relaunch withsystemd-run. Redeploy code =stop(notrestart) + relaunch.- point StandardOutput at the job's canonical log file (what the UI/SSE tails), not a sidecar — observed drift: worker.log vs per-run log.txt diverged.
- foreground
(cmd &)and(cmd &)subshells die with the tool call's group.
Worker takeover law (queue daemons)
A supervisor killed mid-run leaves queue.json.active set ⇒ every relaunching
worker honors "one run at a time" and exits ⇒ silent queue deadlock. Cure:
- heartbeat: run dir
worker.pid, rewritten every ~5s while running, removed on completion; - claim rule:
activeset + pid dead (process.kill(pid,0)ESRCH; EPERM = alive) ⇒ resume; pid alive ⇒ exit. - resume must be append-only + completed-key skip so takeover is cost-free (observed: 386 completed samples skipped, zero re-requests).
Watcher (one async job, never poll)
ssh host 'while [ ! -f /home/dank/logs/NAME.rc ]; do sleep 60; done; \
echo RC=$(cat /home/dank/logs/NAME.rc); tail -25 /home/dank/logs/NAME.log'
Run it async with timeout 0; the result is injected on completion.
Forensics when a detached job died
ps -p PID— gone.- Log 0 bytes → buffered stdout lost at SIGKILL.
journalctl --since … | grep -E "session-[0-9]+.*(Deactivated|Removed session)"— timestamps matching the death window confirm the scope kill (vs OOM, which leaves kernel lines).
Notes
- zsh remote shells:
echo ===X===fails (=cmdexpansion); quote sentinels. - The HF upload of 2026-08 (113.7 GiB, hf_xet) died this way ~2 min in; relaunch as above succeeded.
- Frank's machine:
systemctl --user terminateis not a verb — usestop.