Skip to content

[RayJob] Don't fail a running Ray job when the submitter exits - #5099

Open
1fanwang wants to merge 1 commit into
ray-project:masterfrom
1fanwang:fix/submitter-timeout-consult-job-status
Open

[RayJob] Don't fail a running Ray job when the submitter exits#5099
1fanwang wants to merge 1 commit into
ray-project:masterfrom
1fanwang:fix/submitter-timeout-consult-job-status

Conversation

@1fanwang

@1fanwang 1fanwang commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Why are these changes needed?

KubeRay treats the submitter finishing as evidence the Ray job is terminal. It isn't. ray job logs --follow, the submitter's last command in K8sJobMode and SidecarMode, returns 0 on any non-abnormal WebSocket close (sdk.py):

elif msg.type == aiohttp.WSMsgType.CLOSED:
    if ws.close_code == aiohttp.WSCloseCode.ABNORMAL_CLOSURE:
        raise RuntimeError(...)
    break                                  # normal close: no error
...
return _log_job_status(client, job_id)     # only logs; never exits non-zero

checkSubmitterFinishedTimeoutAndUpdateStatusIfNeeded then waits 30s, overwrites RUNNING with FAILED, and under shutdownAfterJobFinishes deletes the cluster from under the driver. It also produces the inconsistent status pair in #2314.

Ask the cluster instead of guessing

The timeout can't just be removed: #4091 added it for a status that never resolves after a head restart. Both cases arrive with JobStatus: RUNNING.

A job status is only current while the node running its driver is alive. Ray already reports driver_node_id on the job; this deserializes it and adds an IsNodeAlive check against the dashboard's node list:

if rayJobIsActive(rayJob) {
    alive, err := rayJobDriverIsAlive(ctx, dashboardClient, jobInfo)
    ...
    if alive { return false, nil }   // job is genuinely running; leave it
}

The check moves after the job-info poll, where jobInfo and the client are already available. It compares no timestamps and reads no Pod lifecycle, so it is unaffected by readiness probes flapping, by a sidecar submitter dying, and by whether the head restarted before or after the submitter exited, earlier revisions got each of those wrong, and Bugbot caught them.

Scoped to RUNNING on purpose: Ray assigns driver_node_id when it schedules the job's supervisor, so a PENDING job has none to check against. Those keep today's behavior rather than being guessed at. An unreachable dashboard returns an error and requeues instead of counting as a dead driver, and a jobInfo that has already gone terminal is left to the normal handling.

The e2e test RayJob has exceed SubmitterFinishedTimeout asserted the old behaviour; it is rewritten to assert the job survives, and renamed.

Related issue number

Addresses #4921. Related: #2314.

Labels

  • doc-updates-required
  • breaking-change

Checks

  • I've made sure the tests are passing.
  • Testing Strategy
    • Unit tests
    • Manual tests

Manual test instructions

kind, rayproject/ray:2.55.1, shutdownAfterJobFinishes: false so the cluster survives for inspection.

Repro: SIGTERM the head's dashboard JobHead module mid-job. That ends the submitter's ray job logs --follow stream without touching the raylet, so the node stays up and the job keeps running.

On unpatched master — a healthy job is failed.

[07:03:09Z] SIGTERM JobHead
[07:03:25Z] submitter=Succeeded/0 | js=RUNNING dep=Running
[07:03:55Z] submitter=Succeeded/0 | js=FAILED  dep=Failed
            reason=JobDeploymentStatusTransitionGracePeriodExceeded

Ray's own view at that moment, while the CR said FAILED:

node 01c291fbf6981517 state=ALIVE
ray job status = RUNNING   driver_node = 01c291fbf6981517
entrypoint process: still running

It then ran to completion, four minutes after KubeRay gave up on it:

[07:04:37Z] ray=RUNNING     CR=FAILED
[07:08:02Z] ray=SUCCEEDED   CR=FAILED

With shutdownAfterJobFinishes on, the cluster is deleted at 07:03:55 and that work is lost.

On this branch — same repro, job survives.

[06:55:11Z] submitter finished
[06:55:41Z] "The RayJob submitter finished but the Ray job is still active on
             a live node; not transitioning to terminal."
             JobStatus=RUNNING DriverNodeID=1fdaa65c52efe92a...
[07:00:02Z] js=SUCCEEDED dep=Complete

The #4091 case still fails, as it must. Killing the head's DashboardAgent takes the node down: the entrypoint process dies and Ray's status freezes at RUNNING. The driver's node stops reporting ALIVE, so the timeout fires exactly as before.

[06:42:58Z] killed DashboardAgent   → node state=DEAD, entrypoint gone
[06:43:31Z] js=FAILED dep=Failed
            reason=JobDeploymentStatusTransitionGracePeriodExceeded

Force-deleting the head is also still terminal:

[06:39:09Z] dep=Failed reason=AppFailed
            "Submitter completed but Ray job not found in RayCluster."

Unit tests

=== RUN   TestCheckSubmitterFinishedTimeoutAndUpdateStatusIfNeeded
    --- PASS: .../submitter_still_running
    --- PASS: .../grace_period_not_yet_elapsed
    --- PASS: .../job_RUNNING_on_a_live_driver_node_is_left_alone
    --- PASS: .../job_PENDING_has_no_driver_node_yet_and_times_out
    --- PASS: .../job_RUNNING_but_the_driver_node_is_gone_times_out
    --- PASS: .../job_RUNNING_with_no_driver_node_reported_times_out
    --- PASS: .../job_never_observed_times_out
    --- PASS: .../fresh_job_info_is_terminal,_CR_is_stale
    --- PASS: .../node_liveness_errors_leave_the_job_alone
--- PASS: TestCheckSubmitterFinishedTimeoutAndUpdateStatusIfNeeded (0.00s)

@1fanwang
1fanwang force-pushed the fix/submitter-timeout-consult-job-status branch 2 times, most recently from 7c42080 to 50c57a2 Compare August 7, 2026 01:19
Comment thread ray-operator/controllers/ray/rayjob_controller.go
@1fanwang
1fanwang force-pushed the fix/submitter-timeout-consult-job-status branch from 50c57a2 to 34a7ed6 Compare August 7, 2026 02:25
@1fanwang
1fanwang marked this pull request as draft August 7, 2026 03:21
@1fanwang
1fanwang marked this pull request as ready for review August 7, 2026 05:38
@1fanwang
1fanwang force-pushed the fix/submitter-timeout-consult-job-status branch from 34a7ed6 to d31576d Compare August 7, 2026 05:58
Comment thread ray-operator/controllers/ray/rayjob_controller.go
@1fanwang
1fanwang marked this pull request as draft August 7, 2026 06:39
@1fanwang
1fanwang force-pushed the fix/submitter-timeout-consult-job-status branch from d31576d to 247d6fb Compare August 7, 2026 07:14
@1fanwang
1fanwang marked this pull request as ready for review August 7, 2026 07:15
Comment thread ray-operator/controllers/ray/rayjob_controller.go Outdated
@1fanwang
1fanwang marked this pull request as draft August 7, 2026 07:45
@1fanwang
1fanwang force-pushed the fix/submitter-timeout-consult-job-status branch from 247d6fb to ddf3116 Compare August 7, 2026 08:04
@1fanwang
1fanwang marked this pull request as ready for review August 7, 2026 09:25
Comment thread ray-operator/controllers/ray/rayjob_controller.go
Comment thread ray-operator/controllers/ray/rayjob_controller_unit_test.go Outdated
@1fanwang
1fanwang force-pushed the fix/submitter-timeout-consult-job-status branch from ddf3116 to f01abb6 Compare August 7, 2026 09:47

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit f01abb6. Configure here.

Comment thread ray-operator/controllers/ray/rayjob_controller.go Outdated
The submitter's exit is treated as evidence that the Ray job should be
terminal, but it isn't. `ray job logs --follow` returns 0 whenever the log
WebSocket closes with a non-abnormal code, so the submitter can finish under
a perfectly healthy job — after printing that the job is still RUNNING. When
that happens KubeRay waits 30s, overwrites the accurate RUNNING with a
fabricated FAILED, and with shutdownAfterJobFinishes deletes the cluster out
from under the driver.

The grace period was added for the opposite problem: after a head restart the
dashboard reports a status that never resolves, so the RayJob would hang.
Both cases reach this code with JobStatus RUNNING, which is why the timeout
cannot simply be removed.

Ask the cluster instead of guessing. A job status is only current while the
node running its driver is alive, so check that node against the dashboard's
node list before giving up. Ray already reports driver_node_id on the job;
this deserializes it and adds an IsNodeAlive call for the check. The check
runs after the job-info poll, where both are available.

This compares no timestamps and reads no Pod lifecycle, so it is unaffected
by readiness probes flapping, by a sidecar submitter dying, and by whether
the head restarted before or after the submitter exited.

Addresses ray-project#4921. Related: ray-project#2314.

Signed-off-by: 1fanwang <1fannnw@gmail.com>
@1fanwang
1fanwang force-pushed the fix/submitter-timeout-consult-job-status branch from f01abb6 to 9f11cf2 Compare August 7, 2026 10:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant