kaxil commented on code in PR #63491:
URL: https://github.com/apache/airflow/pull/63491#discussion_r3982155693


##########
airflow-core/src/airflow/executors/base_executor.py:
##########
@@ -210,6 +216,46 @@ def jwt_generator(self) -> JWTGenerator:
 
         return generator
 
+    def __init_subclass__(cls, **kwargs: Any) -> None:
+        super().__init_subclass__(**kwargs)
+        cls._legacy_warned = set()
+        legacy_workload_types: set[WorkloadType] = set()
+        if cls.__dict__.get("supports_callbacks") is True:
+            _warn_deprecated_executor_usage(
+                f"{cls.__name__}: setting `supports_callbacks = True` as a 
class attribute is "
+                f"deprecated. Add `WorkloadType.EXECUTE_CALLBACK` to 
`supported_workload_types` "
+                f"instead.",
+            )
+            legacy_workload_types.add(WorkloadType.EXECUTE_CALLBACK)
+        if cls.__dict__.get("supports_connection_test") is True:
+            _warn_deprecated_executor_usage(
+                f"{cls.__name__}: setting `supports_connection_test = True` as 
a class attribute is "
+                f"deprecated. Add `WorkloadType.TEST_CONNECTION` to 
`supported_workload_types` "
+                f"instead.",
+            )
+            legacy_workload_types.add(WorkloadType.TEST_CONNECTION)
+        if legacy_workload_types and "supported_workload_types" not in 
cls.__dict__:
+            cls.supported_workload_types = cls.supported_workload_types | 
legacy_workload_types
+        legacy_override_replacements = {

Review Comment:
   `trigger_connection_tests` is the third method this PR removes from a base 
class that shipped it in 3.3.0 and 3.3.1, and it is the only one of the three 
missing from this dict, so an out-of-tree override goes dead with no warning 
while `trigger_tasks` and `order_queued_tasks_by_priority` both get one. Adding 
`"trigger_connection_tests": "trigger_workloads"` covers it in one line.



##########
airflow-core/src/airflow/executors/workloads/base.py:
##########
@@ -32,6 +33,38 @@
     from airflow.executors.workloads.types import WorkloadState
 
 
+class WorkloadType(str, Enum):
+    """Central registry of executor workload types."""
+
+    EXECUTE_TASK = "ExecuteTask"
+    EXECUTE_CALLBACK = "ExecuteCallback"
+    TEST_CONNECTION = "TestConnection"
+
+
+# Central executor priority registry: tuple is ordered from highest priority 
to lowest.
+#
+# Connection tests are short-lived and user-interactive, so they sort ahead of 
tasks:
+# otherwise a sustained task backlog would starve them until the reaper times 
them out.
+#
+# Adding a new workload type is a four-place change that must stay in sync:

Review Comment:
   Two more sites raise on an unrecognised type and are not in this list. 
`BaseExecutor.run_workload` is an isinstance chain over exactly these three 
schemas ending in `raise ValueError(f"Unknown workload type: ...")`, and 
`state_class_for_key` in `workloads/types.py` ends in `raise TypeError`. 
`run_workload` is the worker-side entrypoint for local, Celery and edge3, so a 
fourth type added by following this checklist would queue, sort and dispatch 
cleanly and then fail on every worker.



##########
airflow-core/src/airflow/executors/base_executor.py:
##########
@@ -263,65 +370,44 @@ def log_task_event(self, *, event: str, extra: str, 
ti_key: WorkloadKey):
         self._task_event_logs.append(Log(event=event, task_instance=ti_key, 
extra=extra))
 
     def queue_workload(self, workload: ExecutorWorkload, session: Session) -> 
None:
-        if isinstance(workload, workloads.ExecuteTask):
-            ti = workload.ti
-            self.queued_tasks[ti.key] = workload
-        elif isinstance(workload, workloads.ExecuteCallback):
-            if not self.supports_callbacks:
-                raise NotImplementedError(
-                    f"{type(self).__name__} does not support ExecuteCallback 
workloads. "
-                    f"Set supports_callbacks = True and implement callback 
handling in _process_workloads(). "
-                    f"See LocalExecutor or CeleryExecutor for reference 
implementation."
-                )
-            self.queued_callbacks[workload.key] = workload
-        elif isinstance(workload, workloads.TestConnection):
-            if not self.supports_connection_test:
-                raise NotImplementedError(
-                    f"{type(self).__name__} does not support TestConnection 
workloads. "
-                    f"Set supports_connection_test = True and implement 
connection test handling "
-                    f"in _process_workloads(). See LocalExecutor for reference 
implementation."
-                )
-            self.queued_connection_tests[workload.key] = workload
-        else:
-            raise ValueError(
-                f"Un-handled workload type {type(workload).__name__!r} in 
{type(self).__name__}. "
-                f"Workload must be one of: ExecuteTask, ExecuteCallback, 
TestConnection."
+        if workload.type not in self.supported_workload_types:
+            raise NotImplementedError(
+                f"{type(self).__name__} does not support {workload.type!r} 
workloads. "

Review Comment:
   `workload.type` is a `WorkloadType` member, so `!r` renders 
`<WorkloadType.TEST_CONNECTION: 'TestConnection'>` and the message ends up 
telling the reader to add exactly that to `supported_workload_types`, which is 
not something they can type. `workload.type.value` gives the name they need. 
The tests having to loosen `match="does not support TestConnection workloads"` 
to `match="does not support.*TestConnection"` is this leaking through.



##########
airflow-core/src/airflow/executors/base_executor.py:
##########
@@ -252,6 +303,62 @@ def __repr__(self):
         _repr += ")"
         return _repr
 
+    @property
+    def queued_tasks(self) -> dict:
+        """Backward-compat property: delegates to 
``executor_queues[WorkloadType.EXECUTE_TASK]``."""
+        self._warn_legacy_property(
+            "queued_tasks",
+            "queued_tasks is deprecated. Use 
executor_queues[WorkloadType.EXECUTE_TASK] instead.",
+        )
+        return self.executor_queues[WorkloadType.EXECUTE_TASK]
+
+    @queued_tasks.setter
+    def queued_tasks(self, value: dict) -> None:
+        """Backward-compat setter: writes through to 
``executor_queues[WorkloadType.EXECUTE_TASK]``."""
+        self._warn_legacy_property(
+            "queued_tasks",
+            "queued_tasks is deprecated. Use 
executor_queues[WorkloadType.EXECUTE_TASK] instead.",
+        )
+        self.executor_queues[WorkloadType.EXECUTE_TASK] = value
+
+    @property
+    def queued_callbacks(self) -> dict:
+        """Backward-compat property: delegates to 
``executor_queues[WorkloadType.EXECUTE_CALLBACK]``."""
+        self._warn_legacy_property(
+            "queued_callbacks",
+            "queued_callbacks is deprecated. Use 
executor_queues[WorkloadType.EXECUTE_CALLBACK] instead.",
+        )
+        return self.executor_queues[WorkloadType.EXECUTE_CALLBACK]
+
+    @queued_callbacks.setter
+    def queued_callbacks(self, value: dict) -> None:
+        """Backward-compat setter: writes through to 
``executor_queues[WorkloadType.EXECUTE_CALLBACK]``."""
+        self._warn_legacy_property(
+            "queued_callbacks",
+            "queued_callbacks is deprecated. Use 
executor_queues[WorkloadType.EXECUTE_CALLBACK] instead.",
+        )
+        self.executor_queues[WorkloadType.EXECUTE_CALLBACK] = value
+
+    @property
+    def supports_callbacks(self) -> bool:
+        """Backward-compat property: True if EXECUTE_CALLBACK is in 
supported_workload_types."""
+        self._warn_legacy_property(
+            "supports_callbacks",
+            "supports_callbacks is deprecated. "
+            "Use WorkloadType.EXECUTE_CALLBACK in supported_workload_types 
instead.",
+        )
+        return WorkloadType.EXECUTE_CALLBACK in self.supported_workload_types
+
+    @property
+    def supports_connection_test(self) -> bool:

Review Comment:
   `supports_callbacks` and `supports_connection_test` were plain `bool` class 
attributes in released 3.3.0 and 3.3.1, so `self.supports_callbacks = True` 
inside an executor's `__init__` was legal and is now an `AttributeError` at 
instantiation, with `__init_subclass__` only covering the class-body form. This 
PR added write-through deprecating setters for `queued_tasks` and 
`queued_callbacks` in exactly the same situation, and the newsfragment's "still 
honored" is unqualified, so the same setters here would keep that promise true. 
The two in-tree sites this already broke are `test_scheduler_job.py:13412` and 
`:13646`, both instance assignments the PR had to rewrite.



##########
airflow-core/newsfragments/63491.significant.rst:
##########
@@ -0,0 +1,52 @@
+Unify executor workload queues under ``BaseExecutor.executor_queues``

Review Comment:
   The newsfragment is accurate now, but the PR description has drifted from 
it. The description still opens with "No behavioral change , scheduling order, 
slot accounting ... identically to before", which this file's own "Behavioural 
changes" section contradicts; it documents a `queue_key` property that no 
longer exists anywhere in the tree; and it says four provider executors dropped 
their `queue_workload` overrides when only `KubernetesExecutor`'s was removed, 
since ECS, Batch and Lambda kept theirs behind a version gate. Worth refreshing 
before merge, since the description is what the next reviewer reads first.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to