refactor: remove unnecessary task table join from run status query#486
Open
npow wants to merge 5 commits into
Open
refactor: remove unnecessary task table join from run status query#486npow wants to merge 5 commits into
npow wants to merge 5 commits into
Conversation
…artbeat expired When a run's orchestrator-level heartbeat expires (e.g. after a transient failure in a foreach coordinator) but individual tasks are still heartbeating, the UI was incorrectly showing the run as 'failed' even with active tasks. Fix: add a LEFT JOIN LATERAL that pre-computes max(last_heartbeat_ts) across all tasks for each run, then use it as a fallback in both the `status` and `finished_at` CASE expressions before the final ELSE 'failed'. Uses the same RUN_INACTIVE_CUTOFF_TIME threshold (6 min default) as the run-level heartbeat check for consistency. The join is computed once per run row and the result referenced from both expressions, avoiding the O(runs×tasks) cost that an inline correlated EXISTS would incur on list endpoints (which also call find_records with enable_joins=True). Reproducer: foreach flow with 200+ tasks where one task hits a transient infrastructure error; orchestrator heartbeat lapses but remaining tasks continue. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…tics When a task has an active heartbeat, the run now correctly shows 'running' even if the run-level heartbeat has expired. Update the test that was asserting 'failed' in this scenario — it was written under the old semantics where run heartbeat was the sole signal. Also apply black formatting to pass pre-commit. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The previous fix was too broad: any run with an expired run heartbeat would check task heartbeats and potentially show 'running', even for zombie runs dead for days. Add a staleness bound (RUN_INACTIVE_CUTOFF_TIME * 10 = 3600s): the task-HB fallback only fires when the run heartbeat expired recently. A 7-day-old run heartbeat (604800s >> 3600s) still falls through to 'failed'; a 7-minute-old run heartbeat (420s <= 3600s) gets the task-HB check. Also revert the test assertion that was incorrectly updated to expect 'running' for the zombie-run scenario — it should remain 'failed'. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…oof of liveness The staleness bound on the run heartbeat was unnecessary. The lateral JOIN already constrains to task heartbeats within RUN_INACTIVE_CUTOFF_TIME (360s), so any task HB that passes the check is genuinely fresh. No scenario exists where a <6-min-old task HB is wrong evidence that a task is alive. Restore test assertion to 'running' — the prior 'failed' expectation was documenting the old buggy behavior that this PR is fixing. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The lateral join added in #485 is redundant: the task heartbeat endpoint (tasks_heartbeat) already calls update_heartbeat on both the run table and the task table, so run.last_heartbeat_ts is always kept in sync with the latest task heartbeat. The existing CASE expression checking run.last_heartbeat_ts is therefore sufficient. Also removes the now-unused task_table attribute and MetadataTaskTable import. Closes #485
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.
Summary
The lateral join to the task table added in #485 is redundant and can be removed:
tasks_heartbeatendpoint (/tasks/{task_id}/heartbeat) already callsupdate_heartbeaton both the run table and the task table (seemetadata_service/api/task.pyline 333).run.last_heartbeat_tsis always kept in sync with the latest task heartbeat.WHEN run.last_heartbeat_ts IS NOT NULL AND ... THEN 'running'check is sufficient — it already covers the case where tasks are actively running.Net change vs master: removes the dead
task_tableattribute andMetadataTaskTableimport that were already unused before #485 was written.What this replaces
Supersedes #485. The performance concern raised during that review (40ms × 50 lateral calls, 2s list latency) goes away entirely since there is no join.
Test plan
test_run_status_running_with_heartbeatcovers the running case viarun.last_heartbeat_tstest_run_status_failed_with_heartbeat_expired_and_no_failed_tasksreverted to its original assertion — ifrun.last_heartbeat_tsis expired, the run isfailedregardless of task heartbeats (because in production, task heartbeats also keeprun.last_heartbeat_tsfresh; an expired run HB with a fresh task HB is an impossible steady-state scenario)🤖 Generated with Claude Code