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


##########
airflow-core/src/airflow/api_fastapi/execution_api/routes/task_instances.py:
##########
@@ -677,6 +677,15 @@ def _create_ti_state_update_query_and_update_state(
         if ti_patch_payload.trigger_timeout is not None:
             timeout = timezone.utcnow() + ti_patch_payload.trigger_timeout
 
+        ti = session.get(TI, task_instance_id)
+        if ti is not None and ti.start_date is not None:
+            dag = dag_bag.get_dag_for_run(dag_run=ti.dag_run, session=session)

Review Comment:
   This deserializes the full SerializedDAG on every deferral just to read one 
task attribute (`execution_timeout`). `ti_update_state` with `state=deferred` 
is a hot path -- every deferrable-sensor poke and every deferrable-operator 
await hits it.
   
   The sibling helper `_handle_fail_fast_for_dag` in this same file 
deliberately avoids exactly this:
   
   ```python
   # Check fail_fast from DagModel (simple column lookup) - early exit if False
   # This avoids loading 5-50 MB SerializedDAG in 99% of cases
   fail_fast = session.scalar(select(DagModel.fail_fast).where(DagModel.dag_id 
== dag_id))
   if not fail_fast:
       return
   # Only load SerializedDAG when fail_fast=True (rare case ~1%)
   ser_dag = dag_bag.get_dag_for_run(dag_run=dr, session=session)
   ```
   
   That guard only works because `fail_fast` is a `DagModel` column. 
`execution_timeout` is task-level and lives only in the serialized blob, so 
there's no cheap column to gate on -- every deferral here pays the full 
deserialization unconditionally, even for the tasks (the majority) that set no 
`execution_timeout`.
   
   The worker already has the value with no DB hit: `_run_execute_callable` 
reads `task.execution_timeout`, and the worker already ships 
`trigger_timeout=defer.timeout` in the defer payload (`task_runner.py:1494` -> 
`TIDeferredStatePayload.trigger_timeout`). Compute the execution deadline (or 
the remaining budget) on the worker at defer time and send it in the payload 
the way `trigger_timeout` already flows; the server then does `min(...)` with 
zero deserialization.
   
   Tradeoff: a new payload field needs an Execution API version bump (Cadwyn), 
while the server-side load doesn't touch the wire. But a 5-50 MB 
deserialization on every defer isn't worth saving a versioned field, and it 
regresses the fail-fast optimization this file just added. The extra 
`session.get(TI, ...)` full-ORM reload (the route only selected the columns it 
needed) folds into the same fix.
   



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