Retry transient deadlocks in Trigger.submit_event/submit_failure - #71391
Open
uplsh580 wants to merge 1 commit into
Open
Retry transient deadlocks in Trigger.submit_event/submit_failure#71391uplsh580 wants to merge 1 commit into
uplsh580 wants to merge 1 commit into
Conversation
uplsh580
added a commit
to uplsh580/airflow
that referenced
this pull request
Aug 10, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
uplsh580
added a commit
to uplsh580/airflow
that referenced
this pull request
Aug 10, 2026
uplsh580
force-pushed
the
fix/triggerer-submit-event-deadlock-retry-65818
branch
from
August 10, 2026 23:47
18a9935 to
4aa48eb
Compare
The per-task-instance UPDATE the triggerer issues to resume or fail deferred tasks (Trigger.submit_event / submit_failure -> handle_event_submit) contends with the scheduler's bulk task_instance writes (e.g. check_trigger_timeouts) and with other triggerer replicas. On MySQL/InnoDB a transient deadlock (error 1213) on that UPDATE propagated straight through handle_events and killed the triggerer process, causing a container restart. The bulk-UPDATE paths (Trigger.clean_unused, check_trigger_timeouts) already retry on deadlock; this extends the same treatment to the per-event single-row path by decorating both entry points with @retry_db_transaction, matching the existing scheduler-side pattern. Both are always called without an outer session, so provide_session owns the transaction and a rollback-and-retry is safe; the retried body re-reads the still-deferred rows, so it is idempotent. Related to apache#65818
uplsh580
force-pushed
the
fix/triggerer-submit-event-deadlock-retry-65818
branch
from
August 11, 2026 15:43
4aa48eb to
b4efda8
Compare
uplsh580
added a commit
to uplsh580/airflow
that referenced
this pull request
Aug 11, 2026
uplsh580
marked this pull request as ready for review
August 11, 2026 15:43
uplsh580
force-pushed
the
fix/triggerer-submit-event-deadlock-retry-65818
branch
from
August 11, 2026 16:50
b4efda8 to
ec3e516
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Retry transient MySQL/InnoDB deadlocks on the triggerer's per-event
task_instanceUPDATE so a single(1213, 'Deadlock found ...')no longer takes down the triggerer process.Problem
When the triggerer fires an event, it resumes (or fails) the dependent deferred task instances through a single-row UPDATE:
That UPDATE contends for
task_instancerow locks with the scheduler's bulk writes (e.g.SchedulerJobRunner.check_trigger_timeouts,Trigger.clean_unused) and, with more than one triggerer replica, with the other triggerer(s). On MySQL/InnoDB the lock-acquisition order differs between the set-based and row-by-row writers, so InnoDB occasionally aborts one side with(1213, 'Deadlock found when trying to get lock; try restarting transaction').Neither
submit_event/submit_failurenor anything above them in the triggerer call chain retries or catches this, so theDBAPIErrorpropagates up toTriggerRunnerSupervisor.runand the triggerer process exits. Deferred tasks are picked up by the other replica, so no task fails, but the restart is noisy at the alerting level and (as reported) the process sometimes has to beSIGKILLed.Reproduced on 3.1.7/3.1.8 and still reproducing on 3.2.2. This is the single-row path, which is not covered by the existing bulk-UPDATE PRs (#65836, #65920) or by the triggerer comms-channel fix (#66412).
Fix
Decorate
Trigger.submit_eventandTrigger.submit_failurewith@retry_db_transaction, stacked under@provide_sessionexactly like the existing model-side usages (DagWarning.purge_inactive_dag_warnings,RenderedTaskInstanceFields). This is the same retry-on-deadlock treatment the bulk paths already get viarun_with_db_retries(); it just extends it to the per-event single-row path.This is safe and idempotent here:
@provide_sessionowns the whole transaction — a rollback-and-retry cannot corrupt a caller's transaction.SELECT ... WHERE state == DEFERREDand re-applies the state transition, so re-processing the still-deferred rows is idempotent.This is an incremental mitigation, not a redesign: it does not remove the concurrent-writer contention described in the issue, but it stops a single transient deadlock from killing the triggerer.
Tests
Added two regression tests in
airflow-core/tests/unit/models/test_trigger.pythat inject a1213-styleOperationalErroron the first attempt of each path and assert the transaction is retried and the task instance still ends upSCHEDULED.related: #65818
Was generative AI tooling used to co-author this PR?
Generated-by: Claude Code following the guidelines