Repository navigation
Fix duplicate pending Dag runs for the same asset partition key - #71074
Conversation
|
According to previous discussion #58919 (comment), I don't think we would like to have Dag table locked |
f8e6556 to
4faf5ba
Compare
|
Pushed a rework that drops the Dag-row lock entirely — no The new approach: a unique constraint on Concurrency is now optimistic instead of pessimistic: Test evidence: Drafted-by: Claude Code (Sonnet 5) |
bf37dec to
09fcebf
Compare
|
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed in 5 days if no further activity occurs. Thank you for your contributions. |
|
Hello! We are encountering this issue and have been able to reproduce it on current The reproduction has two parallel producer tasks emitting different assets for the same partition, and a consumer using We also reproduced it deterministically using two real transactions calling Is there still a plan to refresh this PR? Happy to share the reproduction and help validate the updated approach rather than open a duplicate. |
|
I think we'll need rebasing before the next review round. I can take a look next week |
|
A quick update: we tested the current PR head in Breeze with PostgreSQL 14, and it fixes the race we were seeing. We used two real transactions emitting different assets for the same partition, forcing both to look for an APDR before either inserts one. The same test fails on unfixed code with two pending APDRs. With this PR, all five rounds produced one APDR containing both events, and the scheduler created exactly one consumer run for each pair. Sending another pair for the same partition after consumption also worked. The existing manager and migration-helper tests passed alongside our new scenarios. We haven’t tested MySQL concurrency or a full upgrade from an existing database yet, so this isn’t a complete sign-off, but it’s encouraging evidence for the core fix. Let me know if I can assist anymore with this. |
|
create a new PR and do a similiar fix is something we can do since the author might not be intersted in coming back to this. |
|
Ok, I'll get something up soon-ish. |
When events from different producer assets mapped to the same partition key at the same time, each could create its own pending AssetPartitionDagRun. Neither row then collected all the events the consumer needed, so the consumer Dag never ran. The find-or-create locked the producer's asset row, which two different producers never share. A unique constraint on (target_dag_id, pending_partition_key) now stops a second pending row for a Dag that batches asset events, and a writer that loses the INSERT reuses the winning row. pending_partition_key is set only on rows created with batching on and is cleared when the Dag run is created, so Dags without batching still get one row per event. The migration merges existing duplicate pending rows into the latest one and moves their asset event logs across. Co-authored-by: Kaxil Naik <kaxil@astronomer.io>
ca3d2f3 to
4a121a9
Compare
|
@nathadfield Thanks for the reproduction and for testing the earlier head. I've rebased this onto main and pushed to this branch, so no need to open a new PR. Main moved under it: #68517 made asset event batching opt-in, so the unique constraint now only covers Dags that batch events (an Could you re-run your Postgres reproduction against the new head (4a121a9)? If you can also try a MySQL run or an upgrade from an existing 3.3 database with stuck duplicate rows, that would cover the two gaps you mentioned. |
|
@Lee-W Could you take this PR over from here? It's rebased and green locally, but it still needs a review round . Could you also pick up the follow-up from the Known issues section? A deadlock in |
The 0148 migration no longer merges or deletes duplicate pending asset_partition_dag_run rows: Dags without batch_asset_events (the default) legitimately have one row, and one Dag run, per asset event. It only marks the latest pending row per (target_dag_id, partition_key). _get_or_create_apdr now repeats the lookup and INSERT, up to two attempts, when the loser of the unique-constraint race finds that the winner was already consumed by the scheduler. Queued Dags are visited in dag_id order, and the new tests cover both behaviours.
|
Took this over and did another of review. To get this issue merged faster, I make the |
Backport failed to create: v3-4-test. View the failure log Run detailsNote: As of Merging PRs targeted for Airflow 3.X In matter of doubt please ask in #release-management Slack channel.
You can attempt to backport this manually by running: cherry_picker bf022cc v3-4-testThis should apply the commit to the v3-4-test branch and leave the commit in conflict state marking After you have resolved the conflicts, you can continue the backport process by running: cherry_picker --continueIf you don't have cherry-picker installed, see the installation guide. |
closes: #71070
Two producer assets that emit events for the same partition at the same moment can each create a pending
AssetPartitionDagRun. The events end up split across two rows, so a consumer scheduled onasset_a & asset_bwaits forever. The old find-or-create locked the producer'sAssetModelrow, but two different producers lock two different rows.This adds a unique constraint on
(target_dag_id, pending_partition_key). When two writers race, the second INSERT fails inside a savepoint and that writer reuses the row the first one created. If the scheduler already consumed that row before the loser re-selects, its key is released and the lookup finds nothing, so the loser INSERTs again (two attempts in total) instead of failing the task. #58919 already ruled out locking the Dag row, and an advisory lock would serialize unrelated Dags too. I added a test that drives two sessions throughregister_asset_changethe way the race happens in production. It leaves two pending rows on main and one row with both events here, on Postgres and MySQL.pending_partition_keyexists because MySQL has no partial unique index. It holds the partition key while the row is pending, and a@validateshook clears it whencreated_dag_run_idis set. Unique indexes treat NULLs as distinct on all three backends, so consumed rows never collide.Since #68517 made batching opt-in, a Dag without
batch_asset_eventsis supposed to get one APDR per event. Those rows are written with a NULL pending key and the constraint ignores them. The reuse lookup still matches any pending row for the key, so a Dag that switches batching on keeps adding to its latest pending row.Migration
0148(3.4.0) adds the column and the constraint, and setspending_partition_keyon the latest pending row of each (Dag, key). Older duplicate pending rows are left alone: a Dag withoutbatch_asset_eventslegitimately has one row per event, and merging them would collapse several Dag runs into one. Their NULL key never collides with the constraint, and offline mode runs the same SQL. The 3.5.0 migration becomes0149, the same reshuffle #74837 did. A development database already at the old 3.5.0 head (c71065a1b14f) skips0148, so downgrade it past0149and upgrade again.Known issues
An INSERT that hits another transaction's uncommitted row waits for it to finish. If two producer transactions each create new pending rows for the same two (Dag, key) pairs in opposite orders, they deadlock. Sorting target Dags by
dag_idrules that out for the Dags of a single event. It can still happen across a task's outlets, or when two mappers emit the same keys in a different order.ti_update_statemarks the task FAILED on an unexpected exception today, deadlocks included. I'll send a follow-up so a deadlock there returns a 5xx and the worker retries.This needs to go to
v3-4-testbefore 3.4.0 final, since the migration is stamped 3.4.0.