Stop overlapping deferred TI writers on MySQL - #72134
Conversation
Scheduler timeout and triggerer clean_unused scanned task_instance on different indexes and deadlocked. While deferred, only the owning triggerer writes trigger_id. clean_unused no longer bulk-NULLs TIs. Timeout is a PK-ordered SKIP LOCKED fallback for dead or unassigned triggerers. DEFERRED-exit paths including clear_task_instances NULL trigger_id in the same transaction. related: apache#65818
skip-downstream is a DEFERRED-exit that does not go through set_state. Leaving trigger_id set leaks Triggers now that clean_unused no longer bulk-unlinks. Name the timeout fallback batch size so ORDER BY id LIMIT N fails on revert.
|
Follow-up commit
Drafted-by: Cursor Grok 4.6 |
TaskMap.expand_mapped_task writes REMOVED, SKIPPED, and UPSTREAM_FAILED without going through set_state. A deferred extra that keeps trigger_id pins the triggerer after clean_unused stopped bulk-unlinking.
|
Follow-up Drafted-by: Cursor Grok 4.6 |
What is the change?
While a task instance is DEFERRED, only the owning triggerer writes its
trigger_idandnext_*columns. The scheduler'scheck_trigger_timeoutsbecomes a bounded fallback, PK-ordered withLIMIT 100andFOR UPDATE SKIP LOCKED, that only fires for unassigned triggers or dead TriggererJob heartbeats.Trigger.clean_unusedno longer bulk-updatestask_instanceat all.Why did I do it?
related: #65818
related: #71391
related: #72062
related: #70961
The scheduler timeout sweep and the triggerer cleanup both issued unbounded
UPDATE task_instancestatements. Their predicates are disjoint, but InnoDB scans them on different indexes (ti_statevsti_trigger_id) and deadlocks. #71391 added retries onsubmit_event; this PR removes the lock overlap itself.How did I do it?
Every DEFERRED-exit path NULLs
trigger_idin the same transaction:clear_task_instances,TaskInstance.set_state, DagRun timeout, mapped REMOVED and skipped extras, and the Execution API skip-downstream route.submit_eventandsubmit_failureselect with SKIP LOCKED and no-op if another writer holds the row. The MySQL two-step DELETE inclean_unusedre-checks references so a deferral between SELECT and DELETE cannot cascade-delete the TI (#72062). Timeout SET values are unchanged.What's the impact?
MySQL deadlocks between the two sweeps no longer take down the triggerer, and a deferred task whose triggerer is alive is never timed out by the scheduler.
What's the test plan?
New unit tests in
test_scheduler_job.py(healthy triggerer not flipped, dead triggerer fallback, bounded PK order, MySQL-only two-session SKIP LOCKED),test_trigger.py(noUPDATE task_instance, DELETE re-check),test_cleartasks.py,test_mappedoperator.py, and the Execution API skip-downstream test. Each fails on revert.Was generative AI tooling used to co-author this PR?
Generated-by: Cursor Grok 4.6 following the guidelines