Check that external tasks and task groups exist on Airflow 3 in ExternalTaskSensor - #73085
Open
bingqin2 wants to merge 2 commits into
Open
Check that external tasks and task groups exist on Airflow 3 in ExternalTaskSensor#73085bingqin2 wants to merge 2 commits into
bingqin2 wants to merge 2 commits into
Conversation
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.
On Airflow 3,
check_existence=TrueonExternalTaskSensorverified nothing: a worker has no database access, and the Airflow 2 lookup (DagModelplus aDagBagof the file) has no equivalent through the execution API. #72514 tracks this, and #72517 adds the Dag-level lookup (ti.get_dag, Airflow 3.2+). This adds the task and task-group half. It is independent of #72517: it only touches the Airflow 3 branch of the existence check and the trigger, so whichever merges second has a small rebase in_check_for_existence.How it decides
A Dag run's task instances are created together with the run, in the same transaction and from the run's own Dag version:
_create_orm_dagrunadds and flushes the run and callsverify_integritybefore the caller commits, and every persisted creation path goes through it. So once an awaited run exists, its task instances are the version-accurate answer to whether a task is part of that run, and the worker can read them through the sametask-instances/countandtask-instances/statescalls the sensor already polls with. Per awaited run:get_dr_countis 0): keep waiting; the check repeats on later pokes until every awaited run has been seenexternal_task_ids:ExternalTaskNotFoundErrorexternal_task_group_id:get_task_states(task_group_id=...)answers 404 for a group the Dag does not define (ExternalTaskGroupNotFoundError); an empty answer for a run that exists means the group's tasks are not part of that run (same exception)This follows the direction given on #67832: no dedicated existence endpoint, and no answer before a run exists. Missing tasks and groups are configuration errors, so they are raised regardless of
soft_fail, as on Airflow 2.The deferrable path gets the same rule.
WorkflowTriggertakescheck_existence(serialized, defaultFalse, so already-serialized triggers are unaffected) and yields{"status": "not_found", "kind": "task" | "task_group", "message": ...}, whichexecute_completemaps to the matching exception. The Airflow 2 path is untouched, and no execution API version change is needed: the count and states calls exist on every Airflow 3 release.Known limits
_get_group_tasks), so a group renamed after a pinned run was created is reported missing for that run. A follow-up core PR makes_get_group_tasksresolve the group against the version the run resolves to; on servers without it this limitation stays, and it is the existing behaviour of the states endpoint._verify_integrity_if_dag_changed), so a poke between that Dag update and the next scheduler pass sees no instance for a task that appears seconds later. Airflow 2 had the mirror image, since it checked the current file once.execution_date_fnpointing at dates that never run, still wait until timeout, as on Airflow 2.Changes
utils/sensor_helper.py:_check_external_task_existence(api, ...), the decision rule, usable from the worker (ti) and from the triggerer (RuntimeTaskInstance)sensors/external_task.py:_check_for_existence_af3runs the rule on each poke until every awaited run has been checked; the trigger receivescheck_existence;execute_completehandlesnot_foundtriggers/external_task.py:check_existenceparameter, serialized; the existence loop before the state pollingsoft_failignored, group missing, group unknown, other API errors kept, deferrable hands over to the trigger,execute_completemapping) and 3 trigger tests (task missing, group missing, waits for the run)Testing
providers/standard:tests/unit/standard/sensors/test_external_task_sensor.py,tests/unit/standard/triggers/test_external_task.py,tests/unit/standard/utils/test_sensor_helper.py(75 tests on Airflow 3)Part of #72514 (the task and task-group half; #72517 covers the Dag level).
Was generative AI tooling used to co-author this PR?
Generated-by: Claude Code (Claude Fable 5.1) following the guidelines. I reviewed and understand all changes; the tests were run locally as listed above.
🤖 Generated with Claude Code