Skip to content

fix(task_runner): treat DAGRUN_ALREADY_EXISTS as idempotent success on transport-error retry - #68828

Closed
kalluripradeep wants to merge 2 commits into
apache:mainfrom
kalluripradeep:fix/trigger-dagrun-idempotent-retry
Closed

fix(task_runner): treat DAGRUN_ALREADY_EXISTS as idempotent success on transport-error retry#68828
kalluripradeep wants to merge 2 commits into
apache:mainfrom
kalluripradeep:fix/trigger-dagrun-idempotent-retry

Conversation

@kalluripradeep

Copy link
Copy Markdown
Contributor

Summary

Fixes #66905TriggerDagRunOperator can fail with DagRunAlreadyExists after an ambiguous execution-API trigger retry.

Root cause

Client.request() has a tenacity @retry decorator that replays requests on httpx.RequestError (transport-level ambiguities) and HTTP 5xx. When a POST dag-runs/{dag_id}/{run_id} creates the DAG run server-side but the response is lost, the retry receives 409 Conflict. DagRunOperations.trigger() converts that to ErrorResponse(DAGRUN_ALREADY_EXISTS) and returns it to the supervisor, which forwards it to the task runner.

Previously _handle_trigger_dag_run reacted to DAGRUN_ALREADY_EXISTS with skip_when_already_exists=False (the default) by marking the parent task FAILED and not writing the trigger_run_id XCom.

Fix

When DAGRUN_ALREADY_EXISTS is received and skip_when_already_exists=False, the requested run_id already exists — which is the desired end state. Instead of failing, log an info message and fall through to the normal success path: push the trigger_run_id XCom and honour wait_for_completion.

The skip_when_already_exists=True branch is unchanged — it still returns SKIPPED immediately.

Changes

File Change
task-sdk/src/airflow/sdk/execution_time/task_runner.py _handle_trigger_dag_run: change else branch from FAIL to fall-through-to-success
task-sdk/tests/task_sdk/execution_time/test_task_runner.py Update parametrize (False, FAILED)→(False, SUCCESS); add dedicated transport-retry regression test

Testing

When a trigger POST creates the DAG run server-side but the client
observes an httpx.RequestError, the tenacity retry in Client.request()
replays the POST. The server returns 409 Conflict, which the client
converts to ErrorResponse(DAGRUN_ALREADY_EXISTS). Previously
_handle_trigger_dag_run treated this as a fatal conflict and marked the
parent task FAILED, without writing the trigger_run_id XCom.

The requested run_id already exists -- which is exactly the desired
outcome. With skip_when_already_exists=False (the default) there is no
user intent to skip or fail on conflict; the DAGRUN_ALREADY_EXISTS is
purely an artefact of the transport-error retry path.

Fix: when DAGRUN_ALREADY_EXISTS is received and skip_when_already_exists
is False, log an info message and fall through to the normal success path
(XCom push + wait_for_completion handling). The skip_when_already_exists
branch is unchanged -- it still returns SKIPPED immediately.

Fixes apache#66905
…sion test

For test_handle_trigger_dag_run_conflict:
- Change (False, FAILED) to (False, SUCCESS): when skip_when_already_exists=False
  DAGRUN_ALREADY_EXISTS is now treated as idempotent success.
- Update docstring to reflect the new semantics.

Add test_handle_trigger_dag_run_transport_retry_idempotent_success:
- Verifies that DAGRUN_ALREADY_EXISTS after a transport-error retry produces
  TaskInstanceState.SUCCESS and pushes the trigger_run_id XCom.
- Uses side_effect to simulate the two SUPERVISOR_COMMS.send() calls:
  TriggerDagRun (409 path) followed by SetXCom.

Fixes apache#66905
@kalluripradeep

Copy link
Copy Markdown
Contributor Author

Closing in favour of the earlier PR #66911 which covers the same issue and was filed by @hkc-8010 the same day the issue was opened.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TriggerDagRunOperator can fail with DagRunAlreadyExists after ambiguous execution API trigger retry

1 participant