Fail stranded Dag runs instead of holding a max_active_runs slot - #71642
Open
sing1179 wants to merge 2 commits into
Open
Fail stranded Dag runs instead of holding a max_active_runs slot#71642sing1179 wants to merge 2 commits into
sing1179 wants to merge 2 commits into
Conversation
Deleting a task instance leaves its Dag run unable to reach a terminal state. The scheduler's deadlock check would otherwise fail such a run and release its slot, but it was gated on every unfinished task being free of a task-level concurrency limit, so a single task carrying max_active_tis_per_dag suppressed the check for the whole run. On a Dag with max_active_runs=1 that stops scheduling entirely. Those limits are never evaluated as dependencies on this path, so a merely throttled task already reports as runnable and could not have tripped the check; gating on them protected nothing. depends_on_past and deferred states are evaluated as dependencies and are left in place.
|
Congratulations on your first Pull Request and welcome to the Apache Airflow community! If you have any issues or are unsure about any anything please check our Contributors' Guide
|
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.
Deleting a task instance leaves its Dag run unable to reach a terminal state. The scheduler's deadlock check would otherwise fail such a run and release its
max_active_runsslot, but that check is gated onshould_schedule, where every clause is anall(...)— so a single unfinished task carryingmax_active_tis_per_dagormax_active_tis_per_dagrunsuppresses it for the entire run. On a Dag withmax_active_runs=1, scheduling stops completely. The reporter observed four stranded runs in a single day, one of which blocked roughly ten scheduled runs over four hours.Why removing the concurrency clauses is safe
Task-level concurrency limits are never evaluated as dependencies on this path:
DepContext.depsdefaults to an empty set, and both_get_ready_tisand_are_premature_tisbuild a bareDepContext(...).DEFAULT_OPERATOR_DEPSdoes not containTaskConcurrencyDep. That dep appears only inREQUEUEABLE_DEPS,RUNNING_DEPSandSCHEDULER_QUEUED_DEPS, which are scheduler and executor paths.A throttled task therefore still reports its dependencies as met and counts as runnable, so it could not have tripped the deadlock check in the first place. Gating on these limits protected nothing while disabling detection for the whole run.
PoolSlotsAvailableDepcorroborates this. It lives in the same dep sets and produces the same "parked inSCHEDULED" shape, yetshould_schedulenever gated on pool starvation — and that has never produced the false positive the concurrency gate was presumably guarding against.depends_on_pastandDEFERRED/AWAITING_INPUTare deliberately retained. Those are evaluated as dependencies (PrevDagrunDepis inDEFAULT_OPERATOR_DEPS), so such a task can legitimately become runnable later and must not be treated as deadlocked.Scope
This closes one of the three gates described in the issue. Explicitly not addressed here:
depends_on_pastor is deferred.verify_integritypaths remain gated (scheduler_job_runner.pyrequires a Dag version change and is skipped entirely for bundle-versioned runs;taskinstance.pyis unreachable while the run is non-terminal).Failing the run does at least make the second reachable: once the run is terminal, clearing it can restore the missing row.
Upgrade impact
Dag runs already stranded in the metadata database will terminate as
failedthe next time the scheduler evaluates them. Expect a one-off burst of failure callbacks andon_dag_run_failedlistener events on the first scheduler pass after upgrade.Testing
airflow-core/tests/unit/models/test_dagrun.py: 233 passed, 1 pre-existing xfail.test_dagrun_deadlock_with_deleted_ti_and_concurrency_limit— parametrized over both removed attributes, reproducing the issue with an actualsession.delete(ti), which is whatDELETE /api/v2/dags/{dag_id}/dagRuns/{run_id}/taskInstances/{task_id}does. Fails onmain.test_dagrun_no_deadlock_with_throttled_task— pins the safety argument above; it fails ifTaskConcurrencyDepis ever added to the default dep set.test_dagrun_no_deadlock_with_deleted_ti_and_depends_on_pastandtest_dagrun_no_deadlock_with_deleted_ti_and_deferred_task— cover the two retained clauses. Neither had coverage before: removing either clause previously passed the entire file.closes: #71540
Was generative AI tooling used to co-author this PR?
Generated-by: Claude Code (Opus 5) following the guidelines
{pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.