Repository navigation
Conversation
1fanwang
marked this pull request as ready for review
June 24, 2026 18:38
1fanwang
force-pushed
the
durable-trigger-dagrun
branch
from
June 24, 2026 18:43
eab9d05 to
001febf
Compare
This was referenced Jun 24, 2026
seanghaeli
approved these changes
Jun 24, 2026
seanghaeli
left a comment
Contributor
There was a problem hiding this comment.
Looks good, definitely a follow-up to handle deferrable=True will be useful
Contributor
|
It looks like the run_id is not save before trggering the dag_run so in the worst case :
is this the case ? thanks |
amoghrajesh
requested changes
Jun 29, 2026
amoghrajesh
left a comment
Contributor
There was a problem hiding this comment.
Thanks for your efforts on this @1fanwang, requesting changes for the time being to avoid accidental merge till we arrive at a consensus on https://lists.apache.org/thread/q5b7qsjotop023smr1521o2wfmdyrlhr
This was referenced Sep 25, 2026
This was referenced Sep 25, 2026
With wait_for_completion the trigger-and-wait runs in the task runner. A worker crash while polling makes the retry recompute a fresh run_id and trigger a duplicate child run (or fail with DagRunAlreadyExists), even though the run the first attempt started is healthy and still running. The opt-in durable flag persists the triggered run_id to task_state_store before polling, so the retry reconnects to the in-flight run instead of resubmitting. Signed-off-by: Stefan Wang <[email protected]>
Signed-off-by: Stefan Wang <[email protected]>
Signed-off-by: Stefan Wang <[email protected]>
Signed-off-by: Stefan Wang <[email protected]>
1fanwang
force-pushed
the
durable-trigger-dagrun
branch
from
September 25, 2026 20:53
5fbd7f7 to
f99f238
Compare
1 task done
This was referenced Sep 26, 2026
Signed-off-by: 1fanwang <[email protected]>
An earlier commit on this branch re-added the __init__-time _validate_datetime_param call for logical_date, undoing apache#70490. logical_date is a template field, so validating it in __init__ fails the validate-operators-init check and rejects values before rendering. execute() already validates the rendered value. Signed-off-by: 1fanwang <[email protected]>
airflow-core newsfragments are now reserved for significant changes, and none was requested in review for this opt-in flag. Signed-off-by: 1fanwang <[email protected]>
This branch has not been deployed
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.
A TriggerDagRunOperator that waits for its child Dag run (wait_for_completion=True, not deferrable) does not survive a worker crash during the wait. The retry has no record of the run it already started, so it triggers a second child run and the downstream work runs twice. With a fixed run_id the retry fails because that run already exists, even though it is still healthy.
This PR adds an opt-in durable flag, off by default. With durable=True, Airflow saves the id of the triggered run in the task's state store (Airflow 3.3 and later) before the wait starts. A retry reads that id first. If the run is still going, the retry waits on it instead of starting another. If it already finished in an allowed state, the task succeeds. If it failed, was deleted or can't be read, the retry triggers a new run. Deferrable mode is left for a follow-up.
Airflow already does this for operators that submit and poll an external job. Trigger and save:
airflow/task-sdk/src/airflow/sdk/execution_time/task_runner.py
Lines 2123 to 2188 in 4f70903
A crash after the trigger but before the save still leaves a duplicate, the same gap those operators have. Open question: the flag defaults to False, while those operators default to durable.
Alternatives on the dev list (https://lists.apache.org/thread/q5b7qsjotop023smr1521o2wfmdyrlhr): #68952 with #68955 reuse that shared resume code here and work on 2.11 and 3.0 to 3.3; #69135 lets the operator run the trigger itself on 3.3 and later.
Testing done
Each run kills the parent task process with SIGKILL while TriggerDagRunOperator(wait_for_completion=True) polls a child Dag that sleeps, then lets the scheduler retry the task once.
On 4f70903 with airflow standalone, from the root of the checkout, with the script below saved as e2e.sh:
Reproducer source: e2e.sh
The same scenario on Breeze built from main:
Was generative AI tooling used to co-author this PR?
Generated-by: GitHub Copilot CLI (GPT-6 Astra) following the guidelines