Event-driven scheduling: duplicate DAG runs when an AssetWatcher runs on multiple triggerers
#69319
-
The problemWhen you run more than one triggerer (the default for HA), an We hit this on 3.2.1 with a watcher that polls a Git repo's The watcher definition is deduped (one trigger backs the asset), but the events aren't: each triggerer runs its own copy of the watcher, independently notices the same change, emits its own event, and each event creates a run. So it's effectively at-least-once delivery with no built-in way to get "one run per change" for a plain poller. What's already there (and why it doesn't seem to cover this)A couple of adjacent things exist:
So this may already be considered expected behaviour, and part of what I'm asking is whether it is. How this differs from #54491 / #63507Those are scheduler-side: multiple schedulers turning a single event into two runs (fixed with a row-level lock, #60773). Here there are genuinely two events from two triggerers, so that fix doesn't apply. What we do about it todayThe usual "make the consumer idempotent" approach: the DAG's first step skips if it's already processed this particular change. It works, but every author of an event-driven DAG has to reinvent it, and the duplicate run still gets scheduled before it's skipped. Possible directions
Questions
|
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
|
This looks like #65792 more than a new The short version: on 3.2.1, asset triggers were selected differently from task-instance triggers. The task-trigger path filtered to triggers that were unassigned or assigned to a dead triggerer, but the asset-trigger branch just selected asset triggers, so multiple live triggerers could pick up the same watcher. That matches your “N triggerers => N events/runs” symptom pretty closely. In 3.2.2, the asset-trigger query includes the same That behavior should not be considered the intended steady-state semantics. Airflow’s trigger HA model is meant to have a trigger row owned by one live triggerer, with reassignment on failover; duplicate execution can still happen around failures, but “every live triggerer runs the same asset watcher” is the bug #65792 fixed. The PR description calls out exactly that multiple triggerer replicas could pick up the same asset trigger, and it was marked for the Airflow 3.2.2 milestone/backport. (github.com) The practical next step is to test this on 3.2.2 or newer. If the same Git HEAD watcher still produces one event per triggerer there, that would be worth opening as a focused bug with the watcher definition, triggerer count, DB backend, and a snippet of triggerer logs showing the same trigger ID running on multiple triggerers. A dedupe key on For today, consumer idempotency is still the right defensive pattern. Even with the ownership fix, event-driven systems generally need to tolerate retries, triggerer restarts, and external-source redelivery. The difference is that after #65792, idempotency should protect against occasional duplicate delivery, not deterministic duplication proportional to triggerer count. If upgrading is blocked, the safest mitigation is to run only one triggerer capable of handling event-driven asset watchers, accepting the HA tradeoff, or put a compare-and-set checkpoint in the watcher/first task keyed by the Git SHA. That is not as nice as a first-class dedupe key, but it avoids paying for the expensive downstream work twice. If my answer solved your problem, you can click answered the question. I'm really here to help, and along the way I'm also collecting Galaxy Brain badges haha 😆 |
Beta Was this translation helpful? Give feedback.
This looks like #65792 more than a new
AssetWatcherdedupe-key design gap.The short version: on 3.2.1, asset triggers were selected differently from task-instance triggers. The task-trigger path filtered to triggers that were unassigned or assigned to a dead triggerer, but the asset-trigger branch just selected asset triggers, so multiple live triggerers could pick up the same watcher. That matches your “N triggerers => N events/runs” symptom pretty closely. In 3.2.2, the asset-trigger query includes the same
triggerer_id is null or not in alive_triggerer_idsfilter. (raw.githubusercontent.com)That behavior should not be considered the intended steady-state semantics. Airflow’s trigger …