Repository navigation
Description
Apache Airflow Provider versions
apache-airflow-providers-cncf-kubernetes >= 10.15.0
(verified on main; introduced by #61839, released in 10.15.0)
Apache Airflow version
3.x with KubernetesExecutor and 2 or more schedulers.
What happened
PR #61839 added a periodic call to _adopt_completed_pods from inside
KubernetesExecutor.sync(), gated by
[scheduler] orphaned_tasks_check_interval (default 300 s):
_adopt_completed_pods selects every Succeeded pod whose airflow-worker
label is not the current scheduler's label and PATCHes it with the
current scheduler's label so its KubernetesJobWatcher will see the change
and DELETE the pod:
query_kwargs = {
"field_selector": "status.phase=Succeeded",
"label_selector": (
"kubernetes_executor=True,"
f"airflow-worker!={new_worker_id_label},{POD_EXECUTOR_DONE_KEY}!=True"
),
}With N schedulers (N ≥ 2) running concurrently, every
orphaned_tasks_check_interval each scheduler iterates over every Succeeded
pod that doesn't carry its own label and PATCHes it. Schedulers fight
each other:
- Scheduler A relabels every Succeeded pod owned by B and C → A's watcher
receives the update event and starts DELETEing - Scheduler B does the same a few seconds later → relabels A's freshly
patched pods to B → B's watcher takes over - Scheduler C the same
At steady state with high pod churn (we have a user reporting 1000+ pods
created in a couple of minutes — see context below), this manifests as:
- Heavy
PATCH /api/v1/namespaces/.../pods/...traffic against the kube
API server, multiplied by N - Heavy
_list_podstraffic — see Kubernetes Executor List Pods Performance Improvement #35599 for the known per-call cost at
scale (15–30 s with 500 pods, returning the full V1PodList) - Tasks stalling in
scheduled/queuedbecause every scheduler loop
is burning seconds inside_list_pods+patch_namespaced_podand not
doing useful scheduling delete_worker_pods=Falsedoes NOT help — the periodic adoption code
path doesn't gate ondelete_worker_pods, it goes through the watcher's
delete
What you think should happen instead
The periodic _adopt_completed_pods should only adopt pods owned by
schedulers that are no longer alive — i.e., scope the
airflow-worker!=<my_label> query to also exclude airflow-worker IN (<set of currently-active scheduler job IDs>). The set of active
scheduler job IDs is already known to the scheduler via the
SchedulerJob rows. With that scoping:
- Single-scheduler deployment: no behavior change.
- Multi-scheduler deployment: each scheduler only adopts orphaned pods
whose owning scheduler is gone — no thrash, no cross-scheduler
relabeling.
The original goal of #61839 (closing #57553 — completed pods leaking
after scheduler restart) is still achieved.
How to reproduce
- Deploy Airflow 3.x with
KubernetesExecutorand at least 2
schedulers usingcncf.kubernetes >= 10.15.0. - Run a workload that produces enough Succeeded pods to keep them
visible across the 5-minuteorphaned_tasks_check_intervalwindow
(anything sustained for > 5 min works). - Observe
PATCH /api/v1/namespaces/.../pods/...traffic against the
kube API server — each Succeeded pod gets PATCHed by every scheduler
that doesn't currently own its label, on every interval tick. - The reproducer in the original PR (k8s executor - ensure pods cleaned up #61839, Completed Kubernetes Pods not cleared up #57553) still works for
the single-scheduler "completed pods leak after restart" case, so
any fix needs to keep that scenario working.
Operating system
N/A (the bug is in the Python code path, not OS-specific).
Versions of Apache Airflow Providers
apache-airflow-providers-cncf-kubernetes10.15.0 — present onmain.
Deployment
Official Apache Airflow Helm Chart and any other Kubernetes deployment
running multiple schedulers + KubernetesExecutor.
Anything else
- Caught while triaging a user report on the Airflow user mailing list:
3 schedulers, Airflow 3.2.1, "thrashing between the schedulers trying
to PATCH/DELETE pods after task completion" withdelete_worker_pods=False
not helping. Their workaround was to move workloads to Celery, which
bypasses this code path entirely. - Workaround for affected users until a fix lands: set
[scheduler] orphaned_tasks_check_intervalvery high (e.g., 86400)
and rely on a separatedelete_worker_podscron for cleanup. - Original closing-issue / introducing-PR / related-perf-issue:
- Completed Kubernetes Pods not cleared up #57553 (the leak this regression was meant to fix)
- k8s executor - ensure pods cleaned up #61839 (the introducing PR)
- Kubernetes Executor List Pods Performance Improvement #35599 (the underlying
_list_podscost at scale that compounds the impact)
Are you willing to submit PR?
- Yes I am willing to submit a PR! (per the discussion in the user
thread, I offered to draft a fix that scopes the periodic adoption
to dead schedulers only)
Activity
KubernetesExecutor: scope periodic completed-pod adoption to dead sch…