This is an automated email from the ASF dual-hosted git repository.
ferruzzi 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 9ecd53d3a90 Fix Celery workers rejecting deadline callbacks on Airflow
3.2 (#75438)
9ecd53d3a90 is described below
commit 9ecd53d3a90972254839be6ba4b197f958e4fa0b
Author: femii1234 <[email protected]>
AuthorDate: Tue Nov 10 02:22:26 2026 +0000
Fix Celery workers rejecting deadline callbacks on Airflow 3.2 (#75438)
The pre-3.3 fallback handled task workloads but raised ValueError for
ExecuteCallback, so SyncCallback deadline alerts never ran. Dispatch
those callbacks through the Airflow 3.2 helper.
Signed-off-by: Olufemi Anjorin <[email protected]>
---
.../celery/executors/celery_executor_utils.py | 10 ++++++
.../unit/celery/executors/test_celery_executor.py | 42 ++++++++++++++++++++++
2 files changed, 52 insertions(+)
diff --git
a/providers/celery/src/airflow/providers/celery/executors/celery_executor_utils.py
b/providers/celery/src/airflow/providers/celery/executors/celery_executor_utils.py
index 8e3cfa4adca..9642ee3b2a0 100644
---
a/providers/celery/src/airflow/providers/celery/executors/celery_executor_utils.py
+++
b/providers/celery/src/airflow/providers/celery/executors/celery_executor_utils.py
@@ -314,6 +314,16 @@ def _execute_workload_pre_3_3(input: str) -> None:
log_path=workload.log_path,
subprocess_logs_to_stdout=subprocess_logs_to_stdout,
)
+ elif AIRFLOW_V_3_2_PLUS and isinstance(workload,
workloads.ExecuteCallback):
+ # Airflow 3.3 runs callbacks in BaseExecutor.run_workload().
Airflow 3.2 still
+ # queues deadline alerts as ExecuteCallback and provides
execute_callback_workload.
+ execute_callback_workload = getattr(
+ import_module("airflow.executors.workloads.callback"),
+ "execute_callback_workload",
+ )
+ success, error_msg = execute_callback_workload(workload.callback,
log)
+ if not success:
+ raise RuntimeError(error_msg or "Callback execution failed")
else:
raise ValueError(f"CeleryExecutor does not know how to handle
{type(workload)}")
except Exception as e:
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 41e091012dc..dfbb38eefb4 100644
--- a/providers/celery/tests/unit/celery/executors/test_celery_executor.py
+++ b/providers/celery/tests/unit/celery/executors/test_celery_executor.py
@@ -1289,6 +1289,48 @@ def
test_execute_workload_runs_execute_task_before_airflow_3_3(execute_task_work
assert mock_supervise.call_args.kwargs["log_path"] == "test.log"
[email protected](not AIRFLOW_V_3_2_PLUS, reason="ExecuteCallback requires
Airflow 3.2+")
[email protected](
+ ("callback_result", "expected_error"),
+ [
+ pytest.param((True, None), None, id="callback-succeeds"),
+ pytest.param((False, "Callback failed"), "Callback failed",
id="callback-fails"),
+ ],
+)
+def test_execute_workload_runs_execute_callback_before_airflow_3_3(
+ mock_celery_app, monkeypatch, callback_result, expected_error
+):
+ """The pre-3.3 fallback executes ExecuteCallback with the Airflow 3.2
callback helper."""
+ from airflow.executors import workloads
+ from airflow.executors.workloads.callback import CallbackDTO
+
+ workload = workloads.ExecuteCallback(
+ callback=CallbackDTO(
+ id="00000000-0000-0000-0000-000000000003",
+ fetch_method=workloads.CallbackFetchMethod.IMPORT_PATH,
+ data={"path": "tests.callbacks.test_callback", "kwargs": {}},
+ ),
+ dag_rel_path=Path("test_dag.py"),
+ bundle_info=workloads.BundleInfo(name="test-bundle", version=None),
+ token="test-token",
+ log_path="callback.log",
+ )
+ monkeypatch.setattr(celery_executor_utils, "AIRFLOW_V_3_3_PLUS", False)
+ with mock.patch(
+ "airflow.executors.workloads.callback.execute_callback_workload",
+ create=True,
+ return_value=callback_result,
+ ) as mock_execute_callback:
+ execute = celery_executor_utils.execute_workload.__wrapped__
+ if expected_error:
+ with pytest.raises(RuntimeError, match=expected_error):
+ execute(workload.model_dump_json())
+ else:
+ execute(workload.model_dump_json())
+
+ mock_execute_callback.assert_called_once_with(workload.callback,
celery_executor_utils.log)
+
+
@pytest.mark.skipif(not AIRFLOW_V_3_0_PLUS, reason="execute_workload is only
used for Airflow 3+")
@pytest.mark.skipif(AIRFLOW_V_3_3_PLUS, reason="pre-3.3 compatibility path
only applies before Airflow 3.3")
@pytest.mark.parametrize(