1fanwang opened a new issue, #66778:
URL: https://github.com/apache/airflow/issues/66778

   ### Description
   
   When a sensor task with `mode="reschedule"` raises 
`AirflowRescheduleException`, the task supervisor calls the execution API's 
`PATCH /task-instances/{id}/state` endpoint with a `TIRescheduleStatePayload`. 
That handler takes a `SELECT ... FOR UPDATE` on the `TaskInstance` row (to 
serialize against scheduler state writes) and then inserts a `TaskReschedule` 
row in the same transaction.
   
   Under high sensor concurrency (hundreds of sensors with short poke intervals 
against the same DAG run, common in fan-out patterns), multiple workers can 
contend for related rows. With MySQL's default `innodb_lock_wait_timeout = 
50s`, a blocked worker keeps a DB connection in `IDLE_IN_TRANSACTION` state for 
up to 50 seconds before raising `OperationalError(1205) Lock wait timeout 
exceeded` — long enough to stack up against the connection pool limit and cause 
cascading 5xx responses from the API server for the rest of the workload.
   
   Two related issues at the reschedule write site in 
`airflow-core/src/airflow/api_fastapi/execution_api/routes/task_instances.py`:
   
   1. The 50 s lock wait is far longer than the reschedule write should ever 
need. A blocked reschedule should fail fast and retry, not hold a worker 
connection idle for nearly a minute.
   2. The reschedule branch is not wrapped in retry logic, so a transient 
`Error 1213: Deadlock found` or 1205 timeout surfaces to the worker as a task 
failure — even though the operation is idempotent and Airflow already has 
`retry_db_transaction` for exactly this pattern (used on `DagRun`, 
`RenderedTaskInstanceFields`, `DagWarning`, etc.).
   
   ### Reproducer
   
   A DAG with 50 sensor tasks, each `mode="reschedule"`, `poke_interval=10s`, 
all in the same DAG run. With 8+ schedulers/workers and a MySQL backend, 
running this for ~15 minutes reproduces sporadic `OperationalError: (1205, 
'Lock wait timeout exceeded; try restarting transaction')` task failures 
alongside `(1213, 'Deadlock found')` retries that escape the route handler.
   
   ### Proposal
   
   In the `TIRescheduleStatePayload` branch of 
`_create_ti_state_update_query_and_update_state` (and the `ti_update_state` 
route wrapper):
   
   1. Wrap the reschedule write with the existing 
`@retry_db_transaction(retries=10)` decorator so transient deadlocks/lock-wait 
timeouts retry instead of failing the task.
   2. On MySQL, set a per-session `innodb_lock_wait_timeout` of 4 s 
(configurable via a new `[scheduler] reschedule_lock_timeout_seconds` setting) 
before the lock is acquired, and restore the previous value at the end of the 
request. The result: reschedules either succeed quickly, deadlock-retry through 
the decorator, or fail-fast after a few seconds — never blocking the connection 
pool for 50 s.
   
   ### Are you willing to submit a PR?
   
   - [X] Yes I am willing to submit a PR!


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