Fix SSHRemoteJobOperator still orphaning the remote job on cancellation#69490
Open
kaxil wants to merge 1 commit into
Open
Fix SSHRemoteJobOperator still orphaning the remote job on cancellation#69490kaxil wants to merge 1 commit into
SSHRemoteJobOperator still orphaning the remote job on cancellation#69490kaxil wants to merge 1 commit into
Conversation
The detached job runs under `setsid` so it leads its own process group, and `on_kill` signals that group. The wrapper recorded `$!` as the group leader, but `$!` is only the leader when `setsid(1)` runs in place. When job control is on in the launching shell, `setsid(1)` forks (`setsid(2)` cannot create a new session from an existing group leader), so `$!` names the short-lived setsid parent, not the job. Cancellation then signals a dead group, the real command keeps running as an orphan, and the `exit_code` file is never written, so the task stays deferred until the trigger times out. Record the job's own pid instead: right after `setsid(2)`, POSIX guarantees `pid == pgid == sid` for the caller and that identity survives the following `exec`, so `$$` written from inside the job script is always the true PGID, whether or not setsid forked. The launcher no longer records `$!`, and since the pid file is read only at cancellation time (long after submission) the job's own write is always in place first. Make the end-to-end kill test deterministic by forcing job control through a real controlling terminal, exercising the exact fork path that orphaned the job, and remove the `flaky(reruns=5)` marker. Test markers are unique per test and per worker so the suite stays isolated under `pytest -n auto`.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When an
SSHRemoteJobOperatortask is cancelled,on_killsignals the process group recorded in the job's pid file so the whole job tree is torn down. The detached job launches undersetsidto lead its own process group, and the wrapper recorded$!as that group's leader.$!is the group leader only whensetsid(1)runs in place. If job control is on in the launching shell,setsid(1)forks (setsid(2)cannot create a new session from an existing process-group leader), so$!names the short-livedsetsidparent instead of the job. Cancellation then signals a dead group, the real command keeps running as an orphan, and because the wrapper subshell is what writes theexit_codefile, that file never appears and the task stays deferred until the trigger times out.Fix
Record the job's own pid instead of the launcher's
$!. Right aftersetsid(2), POSIX guaranteespid == pgid == sidfor the caller, and that identity survives the followingexec, so$$written from inside the job script is always the true process-group id, whether or notsetsidforked. The launcher no longer records$!at all, andbuild_posix_kill_commandis unchanged.The pid file is read only at cancellation time, in
on_kill, which runs long after submission, so the job's own write is reliably in place before any reader and the launcher does not need to wait for it.Relationship to #68644
#68644 added the
setsid+ process-group-kill behavior but assumed$!was always the group leader. That holds only when the launching shell is not already a process-group leader. Submission runs withget_pty=False, so the default path is unaffected. The orphaning surfaces on remotes where job control ends up on: a controlling terminal, aForceCommandwrapper, orset -min the remote user's shell init.Regression test (follow-up to #69384)
The end-to-end kill test was marked
@pytest.mark.flaky(reruns=5)in #69384 because it depended on incidental process-group timing. It now forces job control through a real controlling terminal (pty.fork()+bash -mc), exercising the exact fork path that orphaned the job, and asserts the recorded pid equals the job's true process-group id before checking teardown. Theflakymarker is removed; test markers are unique per test and per worker so the suite stays isolated underpytest -n auto.Gotcha
On hosts without
setsid(some macOS/BSD images) the job is not a process-group leader, so$$is just its own pid and cancellation degrades to the previous single-process kill, unchanged from before.