Validate KubernetesExecutor launches with external executor IDs - #69782
Validate KubernetesExecutor launches with external executor IDs#69782wolvery wants to merge 16 commits into
Conversation
|
@wolvery This PR has been converted to draft because it does not yet meet our Pull Request quality criteria. Issues found:
What to do next:
Converting a PR to draft is not a rejection — it is an invitation to bring the PR up to the project's standards so that maintainer review time is spent productively. There is no rush — take your time and work at your own pace. We appreciate your contribution and are happy to wait for updates. If you have questions, feel free to ask on the Airflow Slack. Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you. |
db75770 to
6bff389
Compare
|
Ok, fixes done! |
2bfc36b to
697eeb3
Compare
9ae2e2b to
9784637
Compare
17b60ef to
43d4e84
Compare
ed6417a to
fcb7d43
Compare
1e252cd to
9f4c527
Compare
Only reject a task-instance /run when the worker actually presents an external_executor_id. Older Task SDK workers (mid rolling-upgrade, before the field existed) omit it entirely; fencing them would 409 every launch for pre-assigning executors (KubernetesExecutor, CeleryExecutor) until all workers are upgraded. Absent tokens now fall back to state-based validation. Also document that dropping a stale Kubernetes workload intentionally leaves the task instance in place (owned by the newer launch / reclaimed by queued-task timeout or adoption) rather than failing it. Tests: - API: matching token accepted; missing token accepted for old clients. - KubernetesExecutor: matching token creates the pod (positive counterpart to the stale-drop test). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ec54f47 to
6e37f74
Compare

What
Fixes #69760.
This adds DB-backed launch-token validation for KubernetesExecutor task launches using Airflow's existing
external_executor_idfield:external_executor_idlaunch tokensExecuteTaskworkloads and worker/runrequests/execution/task-instances/{id}/runrequests when the current DB token no longer matches the worker/pod tokenThis prevents a stale Kubernetes workload from validly transitioning an obsolete task-instance launch to
RUNNINGafter a newer queued launch has superseded it.Token semantics
This is equality-based launch-token validation, not a classic monotonic fencing token.
Airflow already has a scheduler path for executors that set
pre_assigns_external_executor_id = True. When such a task instance is queued, the scheduler writes a freshexternal_executor_idUUID to thetask_instancerow. This PR makes KubernetesExecutor use that existing capability and preserve that token through the Kubernetes workload lifecycle.The intended invariant is:
So this is a per-launch ownership token / capability token. It is rotated when Airflow queues a new launch by assigning a new
external_executor_id; it is not ordered, and it should not be interpreted as a monotonic distributed-systems fencing counter.Behavior notes
/runstill returns404 not_found; the worker now treats that as a stale launch and exits0without running user code (see Worker stale handling)./runreturns409 stale_executor_launchand the worker treats it as stale in the same way.Worker stale handling
Launch-token validation shrinks the race window but cannot fully close the post-pod-create / pre-worker-start TOCTOU gap: a pod can be created for a task instance that is superseded before the worker calls
/run. Previously such a worker crashed with an unhandledServerResponseError, which surfaced as a failed pod and a false task failure.The task SDK now maps both
/run404 not_foundand409 stale_executor_launchresponses to a single typedTaskInstanceSupersededError. The supervisor catches it, logs a single INFO line, and exits0— the pod endsSucceeded/Completedrather thanError, so the stale launch does not consume a task retry. The stale event carries the oldtry_number, so it cannot clobber the live (higher-try, still-queued) task instance. Genuine worker startup errors are unaffected: they still raise, exit non-zero, surface asError, and consume a retry.This complements the pre-execution requeue added in #69058: residual post-pod-create stale launches exit
0(Succeeded) so no task retry is consumed, while genuine pre-execution pod failures continue to be requeued by that mechanism.ExecuteTaskworkloads. Otherwise the scheduler interprets the event info asexternal_executor_idand can overwrite the UUID launch token with the numeric scheduler job id.external_executor_id. Rows without a token keep the existing state-based validation so rolling upgrades and legacy queued work are not rejected solely because an older worker omitted the new field.Tests
python3 -m compileall ...on modified source and test filesuv run ruff check ...uv run ruff format --check ...git diff --checkairflow-core/.../execution_api/versions/head/test_task_instances.py -k ti_run— 25 passed, 1 skipped (postgres-only)providers/cncf/kubernetes/.../executors/test_kubernetes_executor.py -k "sync or run_next or stale or external_executor"— all passedTaskInstanceSupersededErrorhandling (/run404/409→ clean exit).Validated on a staging KubernetesExecutor deployment under quota pressure: with the fix live, incident worker pods hitting the residual
/run 404race endedSucceeded(0 failed pods, no409 stale_executor_launchsurfaced as crashes), and the/run 404failure rate dropped from ~1.18% to ~0.65% across comparable before/after windows on the same instance.Gen-AI disclosure
Generated-by: GPT following the guidelines: https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
Important
🛠️ Maintainer triage note for @wolvery · by
@potiuk· 2026-07-15 16:00 UTCHelpful heads-up from the maintainers — please address before this PR can be reviewed (see our Pull Request quality criteria):
The ball is in your court — you've been assigned to this PR. Fix the above, then mark it Ready for review.
Automated triage — may be imperfect; a maintainer takes the next look.