ephraimbuddy commented on code in PR #76303:
URL: https://github.com/apache/airflow/pull/76303#discussion_r4454160899


##########
airflow-core/src/airflow/jobs/scheduler_job_runner.py:
##########
@@ -1450,10 +1534,393 @@ def _enqueue_executor_callbacks(self, session: 
Session) -> None:
                     generator=executor.jwt_generator,
                 )
 
-                executor.queue_workload(workload, session=session)
-                callback.state = CallbackState.QUEUED
+                try:
+                    executor.queue_workload(workload, session=session)
+                except NotImplementedError as e:
+                    # Some executors run callbacks without declaring it, so 
ask the executor itself.
+                    self.log.error("Failing callback %s: %s", callback.id, e)
+                    callback.state = CallbackState.FAILED
+                    callback.output = str(e)
+                else:
+                    callback.state = CallbackState.QUEUED
                 session.add(callback)
 
+    def _route_dag_processor_callbacks(self, *, session: Session) -> None:
+        """
+        Queue Dag processor callbacks on executors.
+
+        With routing on, callback sinks store Dag processor callbacks as 
executor callbacks waiting
+        SCHEDULED (see ``ExecutorCallback.from_dag_processor_request``). A 
routed callback is marked
+        QUEUED in the transaction that queues it. One that no executor can run 
is failed with the
+        reason, and one that fails to route is tried again in a later loop, up 
to
+        ``MAX_ROUTED_CALLBACK_TRIES``.
+
+        Callbacks take at most half the free slots globally and on each 
executor, but one while any
+        is free, before the loop queues tasks into the rest: executors send 
callbacks first, and one
+        left waiting in an executor for a slot would age toward its start 
deadline. Callbacks that
+        could be routed but find no free slot wait for a later loop, while 
those no executor can run
+        are failed without waiting for one.
+        """
+        free_slots = self._parallelism - sum(executor.slots_occupied for 
executor in self.executors)

Review Comment:
   The slot counters here are per scheduler: `slots_occupied` counts its own 
executor workloads, and `core.parallelism` applies to each scheduler 
separately. So they aren’t dividing the same logical capacity. But you’re right 
that, with a shared worker pool, this doesn’t guarantee spare worker capacity 
for tasks.
   
   “Globally” meant across this scheduler’s executors, but it reads as 
cluster-wide. I’ll clarify that this is a per-scheduler, per-loop budget, 
including the minimum-one exception, and soften the claim about preventing 
callbacks from blocking tasks.



-- 
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