Skip to content

dora run orphan containment: remaining gaps after #3018 — pre-init window, path: shell nodes, Windows Job Object #2856

Description

@heyong4725

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

  1. dora run dataflow.yaml --uv with any dataflow of a few Python nodes (ours have timer-driven inputs, e.g. dora/timer/millis/10).
  2. 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)
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions