Summary
When the dora run process is killed abnormally (SIGKILL, supervisor timeout, kill to its process group), the dataflow's node processes survive it and busy-spin at 100–170% CPU indefinitely. They never exit on their own — we have observed an orphaned node accumulate 59 CPU-hours before being found.
Environment
- dora-cli 0.5.0,
dora run <graph>.yaml --uv
- macOS 26 (arm64), Python 3.13 nodes (
from dora import Node, pyarrow payloads)
- Nodes are plain Python processes spawned by the runtime (
python3 -u <node>.py)
Reproduction
dora run dataflow.yaml --uv with any dataflow of a few Python nodes (ours have timer-driven inputs, e.g. dora/timer/millis/10).
- Kill the
dora run process hard while the dataflow is running — any of:
kill -9 <pid of dora run>
timeout 30 dora run ... (the pattern that first bit us: a test harness wrapping runs in timeout)
os.killpg(pid, SIGKILL) on the process group dora run was started in (start_new_session=True)
- Observe surviving
python3 -u <node>.py processes: ps aux | awk '$3 > 50'.
The killpg case is notable: the node processes are evidently not (all) in the dora run process group, so even a group kill misses them.
Expected
Node processes exit (or are reaped) when their runtime/daemon dies — ideally via a parent-death signal (PR_SET_PDEATHSIG equivalent; on macOS e.g. a watchdog on the daemon connection) or a bounded read-timeout → exit path.
Actual
Orphaned nodes spin in their event loop at 100–170% CPU each, apparently busy-polling the dead dataflow's channels, forever. On a laptop-class machine a handful of orphans dominate the cores and silently corrupt any timing-sensitive workload that runs afterwards (this is how we usually notice — unexplained perf regressions, then ps shows orphans from hours or days earlier).
Relationship to existing issues
Workaround we use
We launch dora run with cwd set to a per-run directory and, after every run, kill any process whose working directory is that run dir. It works for supervised runs but misses anything killed before the reaper fires, which is how the 59-CPU-hour orphan happened.
Happy to provide a minimal reproduction dataflow or logs if useful.
Summary
When the
dora runprocess is killed abnormally (SIGKILL, supervisor timeout,killto its process group), the dataflow's node processes survive it and busy-spin at 100–170% CPU indefinitely. They never exit on their own — we have observed an orphaned node accumulate 59 CPU-hours before being found.Environment
dora run <graph>.yaml --uvfrom dora import Node, pyarrow payloads)python3 -u <node>.py)Reproduction
dora run dataflow.yaml --uvwith any dataflow of a few Python nodes (ours have timer-driven inputs, e.g.dora/timer/millis/10).dora runprocess hard while the dataflow is running — any of:kill -9 <pid of dora run>timeout 30 dora run ...(the pattern that first bit us: a test harness wrapping runs intimeout)os.killpg(pid, SIGKILL)on the process groupdora runwas started in (start_new_session=True)python3 -u <node>.pyprocesses:ps aux | awk '$3 > 50'.The killpg case is notable: the node processes are evidently not (all) in the
dora runprocess group, so even a group kill misses them.Expected
Node processes exit (or are reaped) when their runtime/daemon dies — ideally via a parent-death signal (
PR_SET_PDEATHSIGequivalent; on macOS e.g. a watchdog on the daemon connection) or a bounded read-timeout → exit path.Actual
Orphaned nodes spin in their event loop at 100–170% CPU each, apparently busy-polling the dead dataflow's channels, forever. On a laptop-class machine a handful of orphans dominate the cores and silently corrupt any timing-sensitive workload that runs afterwards (this is how we usually notice — unexplained perf regressions, then
psshows orphans from hours or days earlier).Relationship to existing issues
Workaround we use
We launch
dora runwithcwdset to a per-run directory and, after every run, kill any process whose working directory is that run dir. It works for supervised runs but misses anything killed before the reaper fires, which is how the 59-CPU-hour orphan happened.Happy to provide a minimal reproduction dataflow or logs if useful.