This is an automated email from the ASF dual-hosted git repository.
henry3260 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 4e02d39e16b Remove deprecated cleanup_stuck_queued_tasks from Celery
and Kubernetes executors (#76129)
4e02d39e16b is described below
commit 4e02d39e16b9c02ab4ebad7f3ea50916cbe36053
Author: Shahar Epstein <[email protected]>
AuthorDate: Sat Oct 31 12:49:40 2026 +0200
Remove deprecated cleanup_stuck_queued_tasks from Celery and Kubernetes
executors (#76129)
Remove the cleanup_stuck_queued_tasks methods of CeleryExecutor,
CeleryKubernetesExecutor, KubernetesExecutor and LocalKubernetesExecutor,
deprecated since 2024-11-16 in #43520. Use revoke_task instead.
---
providers/celery/docs/changelog.rst | 3 ++
.../providers/celery/executors/celery_executor.py | 19 -------
.../celery/executors/celery_kubernetes_executor.py | 19 +------
.../unit/celery/executors/test_celery_executor.py | 38 -------------
providers/cncf/kubernetes/docs/changelog.rst | 3 ++
.../kubernetes/executors/kubernetes_executor.py | 25 +--------
.../executors/local_kubernetes_executor.py | 14 -----
.../executors/test_kubernetes_executor.py | 62 +---------------------
8 files changed, 11 insertions(+), 172 deletions(-)
diff --git a/providers/celery/docs/changelog.rst
b/providers/celery/docs/changelog.rst
index 91818663dd1..60c733fabf1 100644
--- a/providers/celery/docs/changelog.rst
+++ b/providers/celery/docs/changelog.rst
@@ -27,6 +27,9 @@
Changelog
---------
+.. warning::
+ The deprecated ``CeleryExecutor.cleanup_stuck_queued_tasks`` and
``CeleryKubernetesExecutor.cleanup_stuck_queued_tasks`` methods have been
removed. Use ``revoke_task`` instead.
+
3.26.0
......
diff --git
a/providers/celery/src/airflow/providers/celery/executors/celery_executor.py
b/providers/celery/src/airflow/providers/celery/executors/celery_executor.py
index 91d7509da25..6f9f9431fb9 100644
--- a/providers/celery/src/airflow/providers/celery/executors/celery_executor.py
+++ b/providers/celery/src/airflow/providers/celery/executors/celery_executor.py
@@ -35,9 +35,7 @@ from multiprocessing import cpu_count
from typing import TYPE_CHECKING, Any, ClassVar, TypeAlias
from celery import states as celery_states
-from deprecated import deprecated
-from airflow.exceptions import AirflowProviderDeprecationWarning
from airflow.executors.base_executor import BaseExecutor
from airflow.providers.celery.executors import (
celery_executor_utils as _celery_executor_utils, # noqa: F401 # Needed to
register Celery tasks at worker startup, see #63043.
@@ -386,23 +384,6 @@ class CeleryExecutor(BaseExecutor):
return not_adopted_tis
- @deprecated(
- reason="Replaced by function `revoke_task`. Upgrade airflow core to
make this go away.",
- category=AirflowProviderDeprecationWarning,
- )
- def cleanup_stuck_queued_tasks(self, tis: list[TaskInstance]) -> list[str]:
- """
- Remove tasks stuck in queued from executor and fail them.
-
- This method is deprecated. Use `cleanup_tasks_stuck_in_queued` instead.
- """
- reprs = []
- for ti in tis:
- reprs.append(repr(ti))
- self.revoke_task(ti=ti)
- self.fail(self.get_task_key(ti) if
self.supports_task_instance_uuid else ti.key)
- return reprs
-
def revoke_task(self, *, ti: TaskInstance):
key = self.get_task_key(ti) if self.supports_task_instance_uuid else
ti.key
celery_async_result = self.workloads.pop(key, None)
diff --git
a/providers/celery/src/airflow/providers/celery/executors/celery_kubernetes_executor.py
b/providers/celery/src/airflow/providers/celery/executors/celery_kubernetes_executor.py
index 64cee6f7c4b..50c31ebf77c 100644
---
a/providers/celery/src/airflow/providers/celery/executors/celery_kubernetes_executor.py
+++
b/providers/celery/src/airflow/providers/celery/executors/celery_kubernetes_executor.py
@@ -21,9 +21,6 @@ from collections.abc import Sequence
from functools import cached_property
from typing import TYPE_CHECKING, Any
-from deprecated import deprecated
-
-from airflow.exceptions import AirflowProviderDeprecationWarning
from airflow.executors.base_executor import BaseExecutor
from airflow.providers.celery.executors.celery_executor import CeleryExecutor
# noqa: TC001
from airflow.providers.celery.version_compat import AIRFLOW_V_3_0_PLUS
@@ -260,19 +257,6 @@ class CeleryKubernetesExecutor(BaseExecutor):
*self.kubernetes_executor.try_adopt_task_instances(kubernetes_tis),
]
- @deprecated(
- reason="Replaced by function `revoke_task`. Upgrade airflow core to
make this go away.",
- category=AirflowProviderDeprecationWarning,
- action="ignore", # ignoring since will get warning from the nested
executors
- )
- def cleanup_stuck_queued_tasks(self, tis: list[TaskInstance]) -> list[str]:
- celery_tis = [ti for ti in tis if ti.queue != self.kubernetes_queue]
- kubernetes_tis = [ti for ti in tis if ti.queue ==
self.kubernetes_queue]
- return [
- *self.celery_executor.cleanup_stuck_queued_tasks(celery_tis),
-
*self.kubernetes_executor.cleanup_stuck_queued_tasks(kubernetes_tis),
- ]
-
def revoke_task(self, *, ti: TaskInstance):
if ti.queue == self.kubernetes_queue:
try:
@@ -284,7 +268,8 @@ class CeleryKubernetesExecutor(BaseExecutor):
"provider to enable 'stuck in queue' retries and stuck in
queue "
"event logging."
)
- for ti_repr in
self.kubernetes_executor.cleanup_stuck_queued_tasks(tis=[ti]):
+ # Only cncf.kubernetes providers older than 10.0.1 have this
method.
+ for ti_repr in
self.kubernetes_executor.cleanup_stuck_queued_tasks(tis=[ti]): # type:
ignore[attr-defined]
self.log.info(
"task stuck in queued and will be marked failed.
task_instance=%s",
ti_repr,
diff --git
a/providers/celery/tests/unit/celery/executors/test_celery_executor.py
b/providers/celery/tests/unit/celery/executors/test_celery_executor.py
index 2247944d4b6..41e091012dc 100644
--- a/providers/celery/tests/unit/celery/executors/test_celery_executor.py
+++ b/providers/celery/tests/unit/celery/executors/test_celery_executor.py
@@ -35,7 +35,6 @@ from celery import Celery
from celery.result import AsyncResult
from kombu.asynchronous import set_event_loop
-from airflow.exceptions import AirflowProviderDeprecationWarning
from airflow.executors.base_executor import BaseExecutor
from airflow.models.dag import DAG
from airflow.models.taskinstance import TaskInstance, TaskInstanceKey
@@ -418,43 +417,6 @@ class TestCeleryExecutor:
app.control.revoke = mock.MagicMock()
yield app.control.revoke
- @pytest.mark.backend("mysql", "postgres")
- def test_cleanup_stuck_queued_tasks(self, mocker,
clean_dags_dagruns_and_dagbundles, testing_dag_bundle):
- start_date = timezone.utcnow() - timedelta(days=2)
-
- with DAG("test_cleanup_stuck_queued_tasks_failed", schedule=None) as
dag:
- task = BaseOperator(task_id="task_1", start_date=start_date)
-
- if AIRFLOW_V_3_0_PLUS:
- sync_dag_to_db(dag)
- dag_version = DagVersion.get_latest_version(task.dag.dag_id)
- ti = create_task_instance(task=task, run_id=None,
dag_version_id=dag_version.id)
- else:
- ti = TaskInstance(task=task, run_id=None)
- ti.external_executor_id = "231"
- ti.state = State.QUEUED
- ti.queued_dttm = timezone.utcnow() - timedelta(minutes=30)
- ti.queued_by_job_id = 1
- tis = [ti]
- with _prepare_app() as app:
- app.control.revoke = mock.MagicMock()
- executor = celery_executor.CeleryExecutor()
- mock_fail = mocker.patch.object(executor, "fail", autospec=True)
- executor.job_id = 1
- if hasattr(executor, "_register_task"):
- executor._register_task(ti)
- key = executor.get_task_key(ti) if
executor.supports_task_instance_uuid else ti.key
- executor.running = {key}
- executor.workloads = {key: AsyncResult("231")}
- assert executor.has_task(ti)
- with pytest.warns(AirflowProviderDeprecationWarning,
match="cleanup_stuck_queued_tasks"):
- executor.cleanup_stuck_queued_tasks(tis=tis)
- executor.sync()
- assert executor.workloads == {}
- app.control.revoke.assert_called_once_with("231")
- mock_fail.assert_called_once_with(key)
- assert not executor.has_task(ti)
-
@pytest.mark.backend("mysql", "postgres")
@mock.patch("airflow.providers.celery.executors.celery_executor.CeleryExecutor.fail")
def test_revoke_task(self, mock_fail, clean_dags_dagruns_and_dagbundles,
testing_dag_bundle):
diff --git a/providers/cncf/kubernetes/docs/changelog.rst
b/providers/cncf/kubernetes/docs/changelog.rst
index 829cc47ba9e..d431c2a808c 100644
--- a/providers/cncf/kubernetes/docs/changelog.rst
+++ b/providers/cncf/kubernetes/docs/changelog.rst
@@ -33,6 +33,9 @@ Changelog
team's ``[kubernetes_executor]`` config. Like all team config, these don't
fall back to the
un-prefixed section. If your teams connected using the un-prefixed values,
set them for each team.
+.. warning::
+ The deprecated ``KubernetesExecutor.cleanup_stuck_queued_tasks`` and
``LocalKubernetesExecutor.cleanup_stuck_queued_tasks`` methods have been
removed. Use ``revoke_task`` instead.
+
10.25.0
.......
diff --git
a/providers/cncf/kubernetes/src/airflow/providers/cncf/kubernetes/executors/kubernetes_executor.py
b/providers/cncf/kubernetes/src/airflow/providers/cncf/kubernetes/executors/kubernetes_executor.py
index 68f61b03bfa..6728ed3b81b 100644
---
a/providers/cncf/kubernetes/src/airflow/providers/cncf/kubernetes/executors/kubernetes_executor.py
+++
b/providers/cncf/kubernetes/src/airflow/providers/cncf/kubernetes/executors/kubernetes_executor.py
@@ -40,12 +40,11 @@ from queue import Empty, Queue
from typing import TYPE_CHECKING, Any, cast
from uuid import UUID
-from deprecated import deprecated
from kubernetes.client.rest import ApiException
from kubernetes.dynamic import DynamicClient
from sqlalchemy import select
-from airflow.exceptions import AirflowConfigException,
AirflowProviderDeprecationWarning
+from airflow.exceptions import AirflowConfigException
from airflow.executors.base_executor import BaseExecutor
from airflow.providers.cncf.kubernetes.exceptions import
PodMutationHookException, PodReconciliationError
from airflow.providers.cncf.kubernetes.executors.kubernetes_executor_types
import (
@@ -1150,28 +1149,6 @@ class KubernetesExecutor(BaseExecutor):
tis_to_flush.extend(_iter_tis_to_flush())
return tis_to_flush
- @deprecated(
- reason="Replaced by function `revoke_task`. Upgrade airflow core to
make this go away.",
- category=AirflowProviderDeprecationWarning,
- )
- def cleanup_stuck_queued_tasks(self, tis: list[TaskInstance]) -> list[str]:
- """
- Handle remnants of tasks that were failed because they were stuck in
queued.
-
- Tasks can get stuck in queued. If such a task is detected, it will be
marked
- as `UP_FOR_RETRY` if the task instance has remaining retries or marked
as `FAILED`
- if it doesn't.
-
- :param tis: List of Task Instances to clean up
- :return: List of readable task instances for a warning message
- """
- reprs = []
- for ti in tis:
- reprs.append(repr(ti))
- self.revoke_task(ti=ti)
- self.fail(self.get_task_key(ti) if
self.supports_task_instance_uuid else ti.key)
- return reprs
-
def revoke_task(self, *, ti: TaskInstance):
"""
Revoke task that may be running.
diff --git
a/providers/cncf/kubernetes/src/airflow/providers/cncf/kubernetes/executors/local_kubernetes_executor.py
b/providers/cncf/kubernetes/src/airflow/providers/cncf/kubernetes/executors/local_kubernetes_executor.py
index 97505c441ec..ccb7ac80850 100644
---
a/providers/cncf/kubernetes/src/airflow/providers/cncf/kubernetes/executors/local_kubernetes_executor.py
+++
b/providers/cncf/kubernetes/src/airflow/providers/cncf/kubernetes/executors/local_kubernetes_executor.py
@@ -20,9 +20,6 @@ from __future__ import annotations
from collections.abc import Sequence
from typing import TYPE_CHECKING
-from deprecated import deprecated
-
-from airflow.exceptions import AirflowProviderDeprecationWarning
from airflow.executors.base_executor import BaseExecutor
from airflow.providers.cncf.kubernetes.version_compat import AIRFLOW_V_3_0_PLUS
from airflow.providers.common.compat.sdk import conf
@@ -243,17 +240,6 @@ class LocalKubernetesExecutor(BaseExecutor):
*self.kubernetes_executor.try_adopt_task_instances(kubernetes_tis),
]
- @deprecated(
- reason="Replaced by function `revoke_task`. Upgrade airflow core to
make this go away.",
- category=AirflowProviderDeprecationWarning,
- action="ignore", # ignoring since will get warning from the nested
executors
- )
- def cleanup_stuck_queued_tasks(self, tis: list[TaskInstance]) -> list[str]:
- # LocalExecutor doesn't have a cleanup_stuck_queued_tasks method, so we
- # will only run KubernetesExecutor's
- kubernetes_tis = [ti for ti in tis if ti.queue ==
self.KUBERNETES_QUEUE]
- return
self.kubernetes_executor.cleanup_stuck_queued_tasks(kubernetes_tis)
-
def revoke_task(self, *, ti: TaskInstance):
if ti.queue == self.KUBERNETES_QUEUE:
self.kubernetes_executor.revoke_task(ti=ti)
diff --git
a/providers/cncf/kubernetes/tests/unit/cncf/kubernetes/executors/test_kubernetes_executor.py
b/providers/cncf/kubernetes/tests/unit/cncf/kubernetes/executors/test_kubernetes_executor.py
index 568747c038e..e190df057a1 100644
---
a/providers/cncf/kubernetes/tests/unit/cncf/kubernetes/executors/test_kubernetes_executor.py
+++
b/providers/cncf/kubernetes/tests/unit/cncf/kubernetes/executors/test_kubernetes_executor.py
@@ -3188,62 +3188,6 @@ class TestKubernetesExecutor:
assert not mock_kube_client.patch_namespaced_pod.called
assert tis_to_flush_by_key == {"foobar": {}}
- @pytest.mark.db_test
-
@mock.patch("airflow.providers.cncf.kubernetes.executors.kubernetes_executor.DynamicClient")
- def test_cleanup_stuck_queued_tasks(self, mock_kube_dynamic_client,
dag_maker, create_dummy_dag, session):
- """
- This verifies legacy behavior. Remove when removing
``cleanup_stuck_queued_tasks``.
-
- It's expected that method, ``cleanup_stuck_queued_tasks`` will patch
the pod
- such that it is ignored by watcher, delete the pod, remove from
running set, and
- fail the task.
-
- """
- mock_kube_client = mock.MagicMock()
- mock_kube_dynamic_client.return_value = mock.MagicMock()
- mock_pod_resource = mock.MagicMock()
- mock_kube_dynamic_client.return_value.resources.get.return_value =
mock_pod_resource
- mock_kube_dynamic_client.return_value.get.return_value = k8s.V1PodList(
- items=[
- k8s.V1Pod(
- metadata=k8s.V1ObjectMeta(
- annotations={
- "dag_id": "test_cleanup_stuck_queued_tasks",
- "task_id": "bash",
- "run_id": "test",
- "try_number": 0,
- },
- labels={
- "role": "airflow-worker",
- "dag_id": "test_cleanup_stuck_queued_tasks",
- "task_id": "bash",
- "airflow-worker": 123,
- "run_id": "test",
- "try_number": 0,
- },
- ),
- status=k8s.V1PodStatus(phase="Pending"),
- )
- ]
- )
- create_dummy_dag(dag_id="test_cleanup_stuck_queued_tasks",
task_id="bash", with_dagrun_type=None)
- dag_run = dag_maker.create_dagrun()
- ti = dag_run.task_instances[0]
- ti.state = State.QUEUED
- ti.queued_by_job_id = 123
- session.flush()
-
- executor = self.kubernetes_executor
- executor.job_id = 123
- executor.kube_client = mock_kube_client
- executor.kube_scheduler = mock.MagicMock()
- ti.refresh_from_db()
- tis = [ti]
- with pytest.warns(DeprecationWarning,
match="cleanup_stuck_queued_tasks"):
- executor.cleanup_stuck_queued_tasks(tis=tis)
- executor.kube_scheduler.delete_pod.assert_called_once()
- assert executor.running == set()
-
@pytest.mark.db_test
@mock.patch("airflow.providers.cncf.kubernetes.executors.kubernetes_executor.DynamicClient")
def test_revoke_task(self, mock_kube_dynamic_client, dag_maker,
create_dummy_dag, session):
@@ -5099,7 +5043,7 @@ class TestKubernetesExecutorUuid:
session.scalar.assert_not_called()
client.read_namespaced_pod.assert_not_called()
- def
test_cleanup_stuck_queued_tasks_fails_uuid_and_preserves_other_attempt(self,
uuid_workload, mocker):
+ def test_revoke_task_discards_uuid_and_preserves_other_attempt(self,
uuid_workload, mocker):
executor = KubernetesExecutor()
executor.kube_scheduler =
mocker.create_autospec(AirflowKubernetesScheduler, instance=True)
key = executor.get_task_key(uuid_workload.ti)
@@ -5107,11 +5051,9 @@ class TestKubernetesExecutorUuid:
executor.running.update([key, successor])
mocker.patch.object(executor, "_list_pods", autospec=True,
return_value=[])
- with pytest.warns(DeprecationWarning,
match="cleanup_stuck_queued_tasks"):
- executor.cleanup_stuck_queued_tasks([uuid_workload.ti])
+ executor.revoke_task(ti=uuid_workload.ti)
assert executor.running == {successor}
- assert executor.event_buffer == {key: (State.FAILED, None)}
@pytest.mark.parametrize("native", [False, True])
def test_executor_start_passes_selected_identity_mode_to_scheduler(self,
mocker, monkeypatch, native):