Forum Discussion
Can parallel pipeline runs upsert into the same Lakehouse table without commit conflicts?
- 6 days ago
Thanks a lot, this was very helpful. Your prediction was right, and I could reproduce it.
OPTIMIZE without a partition: After I ran OPTIMIZE on the unpartitioned table, it had merged several _db_id values into shared files. Parallel upserts then failed with ConcurrentDeleteDeleteException (not ConcurrentDeleteReadException), and the Copy sink did not retry it on its own.
What I did: The Copy sink refuses a partition column that is also a key column (KeyColumnIntersectsWithPartitionColumn). If I drop _db_id from the key, the sink matches across partitions and silently overwrites other databases' rows (my databases share the same IDs). So I added a second column with the same value (_db_partition, an additional column in the Copy source) and partitioned by that column. The key stays _db_id + primary key.
What I tested:
- 3 databases loaded one after another into the partitioned table: each kept all its rows, one partition and one file per database.
- OPTIMIZE on a partitioned incremental table compacted the files within each partition (34 files down to 5) and mixed no databases.
- 4 databases in parallel re-upserted already loaded windows into those compacted files: no errors, no duplicates, and all row counts matched.
What I'm doing from here:
- OPTIMIZE and VACUUM only as the last step of the orchestrating pipeline, never on a separate schedule. Not tested: OPTIMIZE running at the same time as an ingest of the same database.
- Keeping the Copy retries.
- Your staging pattern stays as the fallback if conflicts show up in production.
Hi Microfyx
Thank you for sharing the details of your scenario.
Based on the information available, I would recommend avoiding parallel Upsert operations against the same Lakehouse Delta table until the Copy sink's concurrency behavior has been verified. Although each pipeline run uses a different _db_id, this alone does not guarantee that concurrent transactions will be conflict-free.
Here are the recommended considerations:
Avoid parallel upserts into the same Lakehouse table until you verify the Copy sink's concurrency behavior. Different _db_id values do not guarantee conflict-free writes.
Best practice: Ingest all 10 DB2 sources in parallel into staging tables using Append, then run a single sequential MERGE into the Bronze tables using _db_id and the source primary key.
Partitioning by _db_id may help reduce conflicts, but only if the Copy sink's merge predicate enables effective partition pruning.
Retries: Microsoft's documentation does not confirm the Copy sink's automatic retry behavior for Delta commit conflicts.
Concurrency control for Delta tables - Microsoft Fabric | Microsoft Learn