Fix duplicate pending Dag runs for the same asset partition key - #71074
Fix duplicate pending Dag runs for the same asset partition key#71074anmolxlight wants to merge 3 commits into
Conversation
|
According to previous discussion #58919 (comment), I don't think we would like to have Dag table locked |
f8e6556 to
4faf5ba
Compare
…table The previous fix for duplicate AssetPartitionDagRun rows serialized find-or-create behind a lock on the target DagModel row (SQLite global writer lock, or a Postgres/MySQL row-level lock), which contends with unrelated scheduler work touching that Dag. Replace it with a unique constraint on (target_dag_id, pending_partition_key): the database itself rejects the losing INSERT on a race, and the loser catches the IntegrityError and re-selects the winning row instead of raising. This is lock-free and avoids taking a Dag-row lock for a resource (the APDR) that has nothing to do with the Dag row itself. pending_partition_key mirrors partition_key while created_dag_run_id is null and is cleared once the dag run is created, since MySQL supports neither partial nor filtered unique indexes.
|
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) |
closes #71070
Summary
When two different producer assets map to the same downstream partition key (e.g. via
IdentityMapper), each asset event could create its ownAssetPartitionDagRunrow. The scheduler then held two Pending Dag runs for the same partition key and never triggered the consumer Dag.Root cause
AssetManager._get_or_create_apdrserialized APDR find-or-create with_lock_asset_model, which locks the producerAssetModelrow. But APDR dedup is keyed on(target_dag_id, partition_key). Two events from different producer assets that resolve to the same target key therefore took locks on two different asset rows, neither blocked the other, and both observed "no existing APDR" and inserted a duplicate. The reporter's two rows created ~1ms apart with one carryingpartition_dateand the other empty match this exactly: the two events came from different producers, and only one carried a date.Fix
Lock the target
DagModelrow instead (_lock_target_dag). All APDR find-or-create calls for a given consumer Dag now serialize on the same resource regardless of which producer asset triggered the event, so the second event finds the APDR created by the first and dedups onto it. SQLite still uses the global writer lock (unchanged semantics), and the existing "work on the latest matching APDR" fallback in the UI route and scheduler cleanup is preserved.Test
Added
test_queue_partitioned_dags_dedups_across_different_producer_assets: tworegister_asset_changecalls from different producer assets with the same partition key must both lock the same target Dag and yield exactly oneAssetPartitionDagRun.Verified locally with breeze:
6 passed(including the new test and all existing_get_or_create_apdrpartition-date tests), ruff clean, formatting clean.