I'm using Azure SQL + Java + Spring Boot 2. Currently I'm trying to understand the reason why deadlocks occur. In transaction I'm doing insert and later update on the same table by different rows. As far as I understood, SQL Server by default uses rowlock and the read_commited isolation. Here are deadlock details:
<deadlock>
<victim-list>
<victimProcess id="process1991308a4e8"/>
</victim-list>
<process-list>
<process id="process1991308a4e8" taskpriority="0" logused="1104"
waitresource="KEY: 11:72057594043891712 (45e31f8447de)" waittime="3092" ownerId="5354390"
transactionname="implicit_transaction" lasttranstarted="2019-02-13T15:22:55.570" XDES="0x19918794420"
lockMode="U" schedulerid="1" kpid="85796" status="suspended" spid="147" sbid="0" ecid="0" priority="0"
trancount="2" lastbatchstarted="2019-02-13T15:22:56.450" lastbatchcompleted="2019-02-13T15:22:56.340"
lastattention="1900-01-01T00:00:00.340" clientapp="Microsoft JDBC Driver for SQL Server"
hostname="xxx-service-7ff786b7c7-kklgm" hostpid="0" loginname="xxx_core_user"
isolationlevel="read committed (2)" xactid="5354390" currentdb="11" currentdbname="payment"
lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
<executionStack>
<frame procname="unknown" queryhash="0x49c416d3b96f3c35" queryplanhash="0x6ebf9e54f1242ad5" line="1"
stmtstart="510" stmtend="1096"
sqlhandle="0x02000000e96bb62c3a246f941a8767e52b870246866daefc0000000000000000000000000000000000000000">
unknown
</frame>
<frame procname="unknown" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="1"
sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown
</frame>
</executionStack>
<inputbuf>
(@P0 datetime2,@P1 datetime2,@P2 bigint,@P3 nvarchar(4000),@P4 nvarchar(4000),@P5 nvarchar(4000),@P6
nvarchar(4000),@P7 bigint,@P8 int,@P9 nvarchar(4000),@P10 nvarchar(4000),@P11 bigint,@P12
nvarchar(4000),@P13 nvarchar(4000),@P14 nvarchar(4000),@P15 int)update payment set created_at=@P0,
modified_at=@P1, amount=@P2, currency=@P3, description=@P4, email=@P5, external_id=@P6, fee_amount=@P7,
lock_version=@P8, payment_method_provider_type=@P9, payment_method_type=@P10, pos_id=@P11,
pricing_id=@P12, status=@P13 where id=@P14 and lock_version=@P15
</inputbuf>
</process>
<process id="process199130a5088" taskpriority="0" logused="1400"
waitresource="KEY: 11:72057594043891712 (22e8948179bd)" waittime="2842" ownerId="5354401"
transactionname="implicit_transaction" lasttranstarted="2019-02-13T15:22:55.887" XDES="0x19911ddc420"
lockMode="U" schedulerid="1" kpid="8708" status="suspended" spid="156" sbid="0" ecid="0" priority="0"
trancount="2" lastbatchstarted="2019-02-13T15:22:56.557" lastbatchcompleted="2019-02-13T15:22:56.540"
lastattention="1900-01-01T00:00:00.540" clientapp="Microsoft JDBC Driver for SQL Server"
hostname="xxx-service-7ff786b7c7-kklgm" hostpid="0" loginname="xxx_core_user"
isolationlevel="read committed (2)" xactid="5354401" currentdb="11" currentdbname="payment"
lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
<executionStack>
<frame procname="unknown" queryhash="0x49c416d3b96f3c35" queryplanhash="0x6ebf9e54f1242ad5" line="1"
stmtstart="510" stmtend="1096"
sqlhandle="0x02000000e96bb62c3a246f941a8767e52b870246866daefc0000000000000000000000000000000000000000">
unknown
</frame>
<frame procname="unknown" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="1"
sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown
</frame>
</executionStack>
<inputbuf>
(@P0 datetime2,@P1 datetime2,@P2 bigint,@P3 nvarchar(4000),@P4 nvarchar(4000),@P5 nvarchar(4000),@P6
nvarchar(4000),@P7 bigint,@P8 int,@P9 nvarchar(4000),@P10 nvarchar(4000),@P11 bigint,@P12
nvarchar(4000),@P13 nvarchar(4000),@P14 nvarchar(4000),@P15 int)update payment set created_at=@P0,
modified_at=@P1, amount=@P2, currency=@P3, description=@P4, email=@P5, external_id=@P6, fee_amount=@P7,
lock_version=@P8, payment_method_provider_type=@P9, payment_method_type=@P10, pos_id=@P11,
pricing_id=@P12, status=@P13 where id=@P14 and lock_version=@P15
</inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594043891712" dbid="11" objectname="56ae82fa-8057-4f9d-b4a8-290dc2ce3dec.dbo.payment"
indexname="pk_payment_id" id="lock19923e5d980" mode="X" associatedObjectId="72057594043891712">
<owner-list>
<owner id="process199130a5088" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="process1991308a4e8" mode="U" requestType="wait"/>
</waiter-list>
</keylock>
<keylock hobtid="72057594043891712" dbid="11" objectname="56ae82fa-8057-4f9d-b4a8-290dc2ce3dec.dbo.payment"
indexname="pk_payment_id" id="lock19928078f80" mode="X" associatedObjectId="72057594043891712">
<owner-list>
<owner id="process1991308a4e8" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="process199130a5088" mode="U" requestType="wait"/>
</waiter-list>
</keylock>
</resource-list>
</deadlock>
Also I'm using a clustered index on my id column and there are no issues with random operations order.
Moreover when I only do insert inside transaction everything works fine, without a deadlock.
So why am I getting a deadlock?
I cannot share source code but this pseudocode completely represents all logic:
@Transactional
public void processMessage(Message message) {
Entity entity =
repository.findById(message.getId())
.orElseGet(() -> {
Entity entity = converter.convertFrom(message);
return repository.insert(entity); // insert
});
if (canUpdateStatus(entity)) { //let's say it's always true
entity.setStatus(newStatus);
respository.save(entity); // update
}
}