Report language-SDK tasks running while the runtime starts up - #71075
Draft
jason810496 wants to merge 2 commits into
Draft
Report language-SDK tasks running while the runtime starts up#71075jason810496 wants to merge 2 commits into
jason810496 wants to merge 2 commits into
Conversation
jason810496
force-pushed
the
fix/coordinator/mark-running-before-startup
branch
2 times, most recently
from
August 4, 2026 13:46
eb8c17c to
bd0b93d
Compare
1 task
jason810496
force-pushed
the
fix/coordinator/mark-running-before-startup
branch
2 times, most recently
from
August 4, 2026 16:26
ae0f66f to
105bf39
Compare
jason810496
force-pushed
the
fix/coordinator/mark-running-before-startup
branch
from
August 4, 2026 16:31
105bf39 to
bbbcb30
Compare
1 task
A follow-up will let subprocess coordinators heartbeat while materializing a Dag bundle, before the task subprocess exists. That requires the heartbeat state to live outside ActivitySubprocess — especially the pid presented to the server, which must stay identical for the task instance's lifetime or the server rejects the heartbeat as "running elsewhere" and the task is killed. Reactions to fatal heartbeat outcomes (killing the process, recording SERVER_TERMINATED) remain in ActivitySubprocess, injected as callbacks. No behavior change.
The coordinator path left the task queued until the language runtime had connected back to the supervisor. Locating artifacts and waiting out task_startup_timeout were therefore charged to [scheduler] task_queued_timeout, whose handler revokes and requeues the task, and none of it was visible in the UI. A runtime that never started looked like a task that had never been picked up at all, and the stdout and stderr it produced while failing were discarded instead of written to the task log -- for a missing main class or a runtime that is not installed, that output is the whole explanation. The Python path reports the forked child's pid before handing it any work and materializes its Dag bundle inside that window, so this brings the two paths in line. The pid reported here is the supervisor's own because the server rejects a heartbeat whose pid differs from the one it was told at the start, and that is what lets the transition happen before the runtime exists.
jason810496
force-pushed
the
fix/coordinator/mark-running-before-startup
branch
from
August 5, 2026 07:06
bbbcb30 to
03368a2
Compare
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.
Why
The language-SDK subprocess coordinator left the task QUEUED until the runtime had connected back over the comm/logs sockets, so JVM boot, artifact discovery and the whole
task_startup_timeoutwindow were charged to[scheduler] task_queued_timeout, and a runtime that never started looked like a task nothing had ever picked up, with its stdout/stderr discarded.How
Bring the coordinator path in line with the Python path: report RUNNING at the top of
execute_task, before any worker-side preparation.startwhose pid differs.execute_taskcomputes it once and threads it into the RUNNING transition, the startup heartbeats, and the supervised process, where it seedsHeartbeater.pid: the one value every later heartbeat reads, so they cannot drift._heartbeat_until_monitored): a small thread paces theHeartbeaterfrom Extract supervisor heartbeat logic into a standalone Heartbeater #71102, reusing the monitor loop's send and stop policy, so a slow launch is not reaped by[scheduler] task_instance_heartbeat_timeout. With no process to kill yet, the failure cap is left off and transient failures simply retry; a disowned run stops beating and is terminated by the monitor loop moments later.running_sinceis threaded from the transition intoStartupDetails, so the task sees the start date the server recorded.execute_taskreports FAILED (or UP_FOR_RETRY per the run context) and returns a non-zeroExecutionResult, taking the ordinary failed-task path. Warm-shutdown handlers are held across the RUNNING window.What
SubprocessCoordinator.execute_task: report RUNNING before_build_execute_task_command, heartbeat through the launch, report the terminal state when the runtime never starts.ActivitySubprocess: extract the RUNNING transition into_report_runningand accept an optionalreported_pid, seeded intoHeartbeater.pidfor the transition and every heartbeat to read._accept_connections: raise the newSubprocessStartupError(carrying the runtime's exit code) and write pre-handshake stdout/stderr to the task log instead of discarding it.Not addressed here:
_ResourceTracker.__exit__still terminates only the directPopen, so a launcher that forks the real runtime can orphan it on startup failure. A robust fix needsstart_new_sessionplus a process-group kill and belongs in its own PR; this change reduces the exposure from up to three launch attempts to one.No newsfragment: the coordinator feature is itself unreleased on
main.Was generative AI tooling used to co-author this PR?