Medium severityresource
SQL Server Error:
3960
What does this error mean?
Error 3960 fires when two concurrent transactions under SNAPSHOT isolation both read the same row and then try to update it. SQL Server detects the write-write conflict and aborts the second transaction immediately — the first committer wins. In a data-pipeline context (ADF, Synapse, or a custom ETL), this typically surfaces as a failed activity with error code 3960 in the pipeline run output. The engineer sees the pipeline activity turn red, and downstream Copy or Dataflow activities that depend on the failed step are skipped. On the SQL Server side, the aborted session receives Msg 3960 and the transaction is rolled back entirely — no partial writes land in the target table.
Common causes
- 1Two transactions using SNAPSHOT isolation both read the same row and then attempt to update it — SQL Server aborts the second committer with error 3960
- 2Long-running snapshot transactions (e.g. a large MERGE in ADF) that overlap with frequent writes to the same staging or dimension tables
- 3ADF pipeline activity using snapshot isolation conflicts with a concurrent user query or another ETL job writing to the same target table
- 4Concurrent ADF pipeline runs triggered by overlapping tumbling-window or schedule triggers that both update the same partition or key range
- 5Stored procedures called from multiple sessions that SET TRANSACTION ISOLATION LEVEL SNAPSHOT and then perform conditional UPDATEs on a shared lookup table
How to fix it
- 1Step 1: Add retry logic — error 3960 is inherently transient. In ADF, set the activity retry count to 2 and retry interval to 30 seconds in the activity JSON: "policy": { "retry": 2, "retryIntervalInSeconds": 30 }. In application code, catch SqlException where Number == 3960 and retry.
- 2Step 2: Identify which rows cause the conflict. Run: SELECT * FROM sys.dm_tran_version_store_space_usage; and check sys.dm_exec_requests for sessions with isolation_level = 5 (snapshot). Cross-reference the blocking session_id with the object being updated.
- 3Step 3: Shorten the snapshot transaction scope. Break large MERGE statements into batches of 5,000–10,000 rows using a TOP + WHILE loop so each snapshot window is smaller and less likely to overlap with concurrent writes.
- 4Step 4: Switch read-heavy workloads from SNAPSHOT to READ COMMITTED SNAPSHOT (RCSI) — this avoids error 3960 for readers entirely: ALTER DATABASE [YourDB] SET READ_COMMITTED_SNAPSHOT ON; This requires a moment of exclusive DB access (no other connections).
- 5Step 5: Serialize conflicting writes by adding a staging-table pattern. Write ADF output to a staging table first (no contention), then run a single MERGE from staging → target in one short transaction. This eliminates the overlap window.
- 6Step 6: If using concurrent ADF triggers, stagger the schedule so pipeline runs don't overlap. Change the tumbling window from every 15 min to every 15 min offset by pipeline (pipeline A at :00, pipeline B at :05).
- 7Step 7: Monitor tempdb version store usage — snapshot isolation stores row versions in tempdb. If tempdb grows aggressively, check: SELECT * FROM sys.dm_tran_version_store_space_usage; Large version stores correlate with long-running snapshot transactions that increase 3960 risk.
Example log output
Msg 3960, Level 16, State 2, Line 14
Snapshot isolation transaction aborted due to update conflict. You cannot use snapshot isolation to access table 'dbo.dim_customer' directly or indirectly in database 'DW_Production' to update, delete, or insert the row that has been modified or deleted by another transaction. Retry the transaction or change the isolation level for the update/delete statement.