How to Solve Database Deadlock on Microsoft SQL Server

Today, I was helping a team that was getting a database deadlock in their Java Spring Boot application using Microsoft SQL Server. The stacktrace of the error was as shown below.

Exception is org.springframework.dao.CannotAcquireLockException: could not execute query; SQL ; nested exception is org.hibernate.exception.LockAcquisitionException: could not execute query] with root cause
com.microsoft.sqlserver.jdbc.SQLServerException: Transaction (Process ID 132) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

The database deadlock was caused when user uploads a file that was being written to the database. While the file was being uploaded user navigated to another page where data from the same table was queried.

When we debugged SQL Server traces we discovered that row-level lock was escalated to table lock. This was causing SQL Server to stop other transactions from accessing the table.

To solve this issue we enabled READ_COMMITTED_SNAPSHOT option for the database so that it uses READ_COMMITTED with row versioning isolation strategy. The default database isolation level in SQL Server is READ_COMMITTED which means a query in the current transaction cannot read data modified by another transaction that has not yet committed, thus preventing dirty reads. The isolation level uses shared locking or row versioning to prevent dirty reads, depending on whether the READ_COMMITTED_SNAPSHOT database option is enabled. Since wee enabled READ_COMMITTED_SNAPSHOT SQL Server will use versioning instead of locks.

ALTER DATABASE <database name>
   SET READ_COMMITTED_SNAPSHOT ON
   WITH ROLLBACK IMMEDIATE;

Useful Resources

  1. Cause of a process being a deadlock victim – Link
  2. Database deadlock on Microsoft SQL Server – Link
  3. Transaction (Process ID) was deadlocked on lock resources with another process and has been chosen as the deadlock victim – Link