kaxil commented on code in PR #65738:
URL: https://github.com/apache/airflow/pull/65738#discussion_r3501605509


##########
task-sdk/src/airflow/sdk/execution_time/supervisor.py:
##########
@@ -1007,7 +1017,18 @@ def kill(
 
         for sig in escalation_path:
             try:
-                self._process.send_signal(sig)
+                # Signal the whole process group so subprocesses the
+                # task-runner spawned (venv children, Docker exec, bash
+                # shells, etc.) are also reached. Requires the task-runner to
+                # have been placed in its own session via os.setsid() at fork
+                # time (see start()). See issue #65505.
+                try:
+                    os.killpg(os.getpgid(self._process.pid), sig)

Review Comment:
   Thinking about this more: `airflow.utils.process_utils.reap_process_group()` 
already implements this whole teardown, and it has the exact guard that's 
missing here:
   
   ```python
   if not IS_WINDOWS and process_group_id == os.getpgid(0):
       raise RuntimeError("I refuse to kill myself")
   ```
   
   It also covers what this loop doesn't: SIGTERM -> wait -> SIGKILL escalation 
via `psutil.wait_procs`, `EPERM` -> `sudo -n kill` for the `run_as_user` case, 
and `ESRCH` (the "child hasn't changed its group yet" race) by falling back to 
signalling the PID directly.
   
   It lives in airflow-core, and the supervisor keeps its `airflow.*` imports 
lazy for worker isolation, so it's not a drop-in import. But rather than 
hand-rolling a second, less complete version here, should we port/copy 
`reap_process_group` (+ its self-group guard) into task-sdk and use that 
instead? One tested teardown path beats two that can drift.
   



##########
task-sdk/src/airflow/sdk/execution_time/supervisor.py:
##########
@@ -690,6 +690,16 @@ def start(
 
         pid = os.fork()
         if pid == 0:
+            # Put the task-runner into its own session so its PGID == its own
+            # PID. The supervisor can then deliver signals to the whole tree
+            # via os.killpg() in kill(), reaching every subprocess the
+            # task-runner spawned (e.g. venv children from
+            # PythonVirtualenvOperator). Without this, a SIGTERM from kill()
+            # only hits the task-runner and any Popen children are reparented
+            # to PID 1 and leak as orphans. See issue #65505.
+            with suppress(OSError):
+                os.setsid()

Review Comment:
   On the scope point: the existing `set_new_process_group()` 
(`airflow.utils.process_utils`) does this with `os.setpgid(0, 0)` rather than 
`os.setsid()`. That still gives a killable process group (so `killpg` reaches 
the whole tree) but without creating a new session / detaching the controlling 
terminal, so it avoids the Ctrl-C / foreground-group change noted above. Its 
companion `reap_process_group()` already has the SIGTERM -> SIGKILL escalation 
and EPERM/ESRCH handling.
   
   Same question as on the kill() thread: should we port/copy these over into 
task-sdk and use them here instead of a fresh `setsid`/`killpg` path? Reusing 
the existing `setpgid` group + self-group guard would address both the 
over-broad scope and the self-signal risk in one go.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to