I have two SQL queries (MSSQL server):
SELECT [Value]
FROM [dbo].[BigTable]
ORDER BY [Id] DESC
and
UPDATE [dbo].[BigTable]
SET [Value] = [Value]
Where [Id] - Primary clustered key.
When I run them infinitely in the loop I get deadlock, which is obvious. But what is not obvious (for me): why on deadlock graph I get "Owner mode: U" for select statement.
As far as I know select statement can only have shared locks. And here I'm not using any hints or additional transactions to make update lock. Any idea why I see it here?
XML for deadlock is attached
<deadlock-list>
<deadlock victim="process1c094ee5468">
<process-list>
<process id="process1c094ee5468" taskpriority="0" logused="0" waitresource="PAGE: 7:1:1502 " waittime="1289" ownerId="901143" transactionname="SELECT" lasttranstarted="2021-05-05T18:04:54.470" XDES="0x1c094329be8" lockMode="S" schedulerid="6" kpid="22644" status="suspended" spid="62" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2021-05-05T18:04:54.470" lastbatchcompleted="2021-05-05T18:04:54.453" lastattention="1900-01-01T00:00:00.453" clientapp="Core Microsoft SqlClient Data Provider" hostname="ALEXEY-KLIPILIN" hostpid="3132" loginname="sa" isolationlevel="read committed (2)" xactid="901143" currentdb="7" currentdbname="SampleDb" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="adhoc" line="1" stmtend="92" sqlhandle="0x02000000bf49f5138395d042205ae64888add734815151770000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
SELECT * FROM [dbo].[BigTable] ORDER BY Id DESC </inputbuf>
</process>
<process id="process1c096e1d088" taskpriority="0" logused="100" waitresource="PAGE: 7:1:1503 " waittime="1289" ownerId="901139" transactionname="UPDATE" lasttranstarted="2021-05-05T18:04:54.470" XDES="0x1c08bc84428" lockMode="X" schedulerid="4" kpid="9160" status="suspended" spid="61" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2021-05-05T18:04:54.470" lastbatchcompleted="2021-05-05T18:04:54.397" lastattention="1900-01-01T00:00:00.397" clientapp="Core Microsoft SqlClient Data Provider" hostname="ALEXEY-KLIPILIN" hostpid="3132" loginname="sa" isolationlevel="read committed (2)" xactid="901139" currentdb="7" currentdbname="SampleDb" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="adhoc" line="1" stmtend="88" sqlhandle="0x0200000018eeb102d311fd032bb670822f260841060b64410000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
UPDATE [dbo].[BigTable] SET [Value] = [Value] </inputbuf>
</process>
</process-list>
<resource-list>
<pagelock fileid="1" pageid="1502" dbid="7" subresource="FULL" objectname="SampleDb.dbo.BigTable" id="lock1c0884bdd00" mode="X" associatedObjectId="72057594043760640">
<owner-list>
<owner id="process1c096e1d088" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="process1c094ee5468" mode="S" requestType="wait"/>
</waiter-list>
</pagelock>
<pagelock fileid="1" pageid="1503" dbid="7" subresource="FULL" objectname="SampleDb.dbo.BigTable" id="lock1c0a0a23380" mode="U" associatedObjectId="72057594043760640">
<owner-list>
<owner id="process1c094ee5468" mode="S"/>
</owner-list>
<waiter-list>
<waiter id="process1c096e1d088" mode="X" requestType="convert"/>
</waiter-list>
</pagelock>
</resource-list>
</deadlock>
</deadlock-list>
This just looks like some misrepresentation in the graphical representation.
process1c096e1d088 (the UPDATE) holds a page level X lock on page 1502 and a page level U lock on 1503 and is trying to convert that U lock to an X lock. (requestType="convert")
process1c094ee5468 (the SELECT) holds a page level S lock on 1503 (compatible with the U lock) and is waiting for a page level S lock on 1502.
Because the page lock 1503 is held in both S and U modes it has mode="U" in the deadlock XML and the UI assumes it is held by the blocker in that mode.
Of course if the SELECT transaction was to release its lock on 1503 before requesting the lock on 1502 this deadlock could not arise but I assume there is a good reason for it not doing this (maybe to stop 1502 getting deallocated mid scan and leaving it with no next page to visit).
I am facing a keylock deadlock issue in an update statement in SQL Server. I have a clustered index on the primary key and I am using them inside the where clause. I can say for sure that the 2 processes are not updating the same rows as the java code is written like that. They are trying to update different rows but still this deadlock is occurring. Both processes are owner mode of X and requesting for U lock as per the graph. I can share the other as well.
<deadlock-list>
<deadlock victim="process147029c28">
<process-list>
<process id="process147029c28" taskpriority="0" logused="1300"
waitresource="KEY: 7:72057600641925120 (ef40b100fec4)" waittime="6363"
ownerId="7750344" transactionname="implicit_transaction"
lasttranstarted="2018-10-25T21:52:35.553" XDES="0x10006ad90" lockMode="U"
schedulerid="3" kpid="17860" status="suspended" spid="67" sbid="0" ecid="0"
priority="0" trancount="2" lastbatchstarted="2018-10-25T21:52:36.567"
lastbatchcompleted="2018-10-25T21:52:36.540" lastattention="1900-01-
01T00:00:00.540" clientapp="Microsoft JDBC Driver for SQL Server"
hostname="USGURBHABISHT9" hostpid="0" loginname="bharat"
isolationlevel="read committed (2)" xactid="7750344" currentdb="7"
lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
</executionStack>
<inputbuf>
(#P0 nvarchar(4000),#P1 datetime2,#P2 nvarchar(4000),#P3 varchar(8000),#P4
varchar(8000),#P5 nvarchar(4000),#P6 nvarchar(4000),#P7 datetime2,#P8
nvarchar(4000),#P9 varchar(8000),#P10 nvarchar(4000),#P11
varchar(8000),#P12 bigint,#P13 varchar(8000),#P14 varchar(8000),#P15
nvarchar(4000),#P16 varchar(8000),#P17 datetime2,#P18 nvarchar(4000),#P19
nvarchar(4000),#P20 nvarchar(4000),#P21 varchar(8000),#P22
nvarchar(4000),#P23 nvarchar(4000),#P24 nvarchar(4000),#P25
nvarchar(4000),#P26 nvarchar(4000),#P27 nvarchar(4000),#P28 bigint,#P29
nvarchar(4000),#P30 nvarchar(4000),#P31 nvarchar(4000),#P32
nvarchar(4000),#P33 nvarchar(4000),#P34 nvarchar(4000))Update
IN_R_AU_MEM_ELIG_DTL set APPROVAL_CODE = #P0, ARCHIVE_DT = #P1,
CASE_NUMBER = #P2, CATEGORY_CODE = #P3, CG_STATUS_CODE = #P4, CLOSE_DATE =
#P5, CLOSURE_CODE = #P6, CREATE_DT = #P7, CREATE_USER_ID = #P8,
DELETE_INDICATOR = #P9, ELIGIBILITY_SEQUENCE_NUMBER = #P10,
ELIG_INCAR_FLAG = #P11, HISTORY_SEQ = #P12, INCARCERATION_CODE = #P13,
INCARCERATION_DISCHARGE_DATE = </inputbuf>
</process>
<process id="process13e98b088" taskpriority="0" logused="1992"
waitresource="KEY: 7:72057600641925120 (f128991fbbbb)" waittime="6349"
ownerId="7751290" transactionname="implicit_transaction"
lasttranstarted="2018-10-25T21:52:35.803" XDES="0x12f960d90" lockMode="U"
schedulerid="3" kpid="6176" status="suspended" spid="66" sbid="0" ecid="0"
priority="0" trancount="2" lastbatchstarted="2018-10-25T21:52:36.610"
lastbatchcompleted="2018-10-25T21:52:36.603" lastattention="1900-01-
01T00:00:00.603" clientapp="Microsoft JDBC Driver for SQL Server"
hostname="USGURBHABISHT9" hostpid="0" loginname="bharat"
isolationlevel="read committed (2)" xactid="7751290" currentdb="7"
lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
<inputbuf>
(#P0 nvarchar(4000),#P1 datetime2,#P2 nvarchar(4000),#P3 varchar(8000),#P4
varchar(8000),#P5 nvarchar(4000),#P6 nvarchar(4000),#P7 datetime2,#P8
nvarchar(4000),#P9 varchar(8000),#P10 nvarchar(4000),#P11
varchar(8000),#P12 bigint,#P13 varchar(8000),#P14 varchar(8000),#P15
nvarchar(4000),#P16 varchar(8000),#P17 datetime2,#P18 nvarchar(4000),#P19
nvarchar(4000),#P20 nvarchar(4000),#P21 varchar(8000),#P22
nvarchar(4000),#P23 nvarchar(4000),#P24 nvarchar(4000),#P25
nvarchar(4000),#P26 nvarchar(4000),#P27 nvarchar(4000),#P28 bigint,#P29
nvarchar(4000),#P30 nvarchar(4000),#P31 nvarchar(4000),#P32
nvarchar(4000),#P33 nvarchar(4000),#P34 nvarchar(4000))Update
IN_R_AU_MEM_ELIG_DTL set APPROVAL_CODE = #P0, ARCHIVE_DT = #P1,
CASE_NUMBER = #P2, CATEGORY_CODE = #P3, CG_STATUS_CODE = #P4, CLOSE_DATE =
#P5, CLOSURE_CODE = #P6, CREATE_DT = #P7, CREATE_USER_ID = #P8,
DELETE_INDICATOR = #P9, ELIGIBILITY_SEQUENCE_NUMBER = #P10,
ELIG_INCAR_FLAG = #P11, HISTORY_SEQ = #P12, INCARCERATION_CODE = #P13,
INCARCERATION_DISCHARGE_DATE = </inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057600641925120" dbid="7"
objectname="IEWP_EE.dbo.IN_R_AU_MEM_ELIG_DTL"
indexname="IN_R_AU_MEM_ELIG_DTL_PK" id="lock104f34d00" mode="X"
associatedObjectId="72057600641925120">
<owner-list>
<owner id="process13e98b088" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="process147029c28" mode="U" requestType="wait"/>
</waiter-list>
</keylock>
<keylock hobtid="72057600641925120" dbid="7"
objectname="IEWP_EE.dbo.IN_R_AU_MEM_ELIG_DTL"
indexname="IN_R_AU_MEM_ELIG_DTL_PK" id="lock12ac42100" mode="X"
associatedObjectId="72057600641925120">
<owner-list>
<owner id="process147029c28" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="process13e98b088" mode="U" requestType="wait"/>
</waiter-list>
</keylock>
</resource-list>
</deadlock>
</deadlock-list>
Update statement:
Update table_name updating all the columns even the primary key
where 1=1
AND PERSON_NUMBER = '7769999750768'
AND TYPE_CASE_CODE = '550'
AND START_DATE = '20180901'
AND INCARCERATION_ADMIT_DATE = '00000000'
AND OSS_AMOUNT = '000'
AND AID_CATEGORY = '50'
Columns used in the where clause are the composite primary key.
And two update queries deadlocking are having different primary key values.
So how it can have a lock on primary key?
table schema:
CREATE TABLE [dbo].[IN_R_AU_MEM_ELIG_DTL](
[CREATE_USER_ID] [varchar](20) NOT NULL,
[CREATE_DT] [datetime] NOT NULL,
[UNIQUE_TRANS_ID] [bigint] NOT NULL,
[HISTORY_SEQ] [bigint] NOT NULL,
[RECORD_TYPE] [varchar](1) NULL,
[SUB_RECORD_TYPE] [varchar](1) NULL,
[RECORD_SEQUENCE] [varchar](9) NULL,
[PERSON_NUMBER] [varchar](13) NOT NULL,
[PERSON_SEQUENCE] [varchar](8) NULL,
[ELIGIBILITY_SEQUENCE_NUMBER] [varchar](5) NULL,
[CASE_NUMBER] [varchar](13) NULL,
[CATEGORY_CODE] [varchar](2) NULL,
[TYPE_CASE_CODE] [varchar](3) NOT NULL,
[START_DATE] [varchar](8) NOT NULL,
[CLOSE_DATE] [varchar](8) NULL,
[APPROVAL_CODE] [varchar](3) NULL,
[CLOSURE_CODE] [varchar](3) NULL,
[DELETE_INDICATOR] [varchar](1) NULL,
[INCARCERATION_CODE] [varchar](1) NULL,
[INCARCERATION_ADMIT_DATE] [varchar](8) NOT NULL,
[INCARCERATION_DISCHARGE_DATE] [varchar](8) NULL,
[INCARCERATION_ELIG_FLAG] [varchar](1) NULL,
[RENEWAL_DATE] [varchar](8) NULL,
[RENEWAL_CODE] [varchar](2) NULL,
[PRE_RELEASE_DATE] [varchar](8) NULL,
[LOCATION_CODE] [varchar](4) NULL,
[MONEY_CODE] [varchar](1) NULL,
[OSS_AMOUNT] [varchar](5) NOT NULL,
[AID_CATEGORY] [varchar](10) NOT NULL,
[CG_STATUS_CODE] [varchar](5) NULL,
[MMIS_SND_PERSON_SEQ_NUM] [varchar](15) NULL,
[PROCESS_SW] [varchar](1) NULL,
[ELIG_INCAR_FLAG] [varchar](1) NULL,
[ARCHIVE_DT] [datetime] NULL,
[ROWID] [uniqueidentifier] NOT NULL DEFAULT (newid()),
[MMIS_SND_DT] [datetime] NULL,
CONSTRAINT [IN_R_AU_MEM_ELIG_DTL_PK] PRIMARY KEY CLUSTERED
(
[INCARCERATION_ADMIT_DATE] ASC,
[AID_CATEGORY] ASC,
[OSS_AMOUNT] ASC,
[PERSON_NUMBER] ASC,
[START_DATE] ASC,
[TYPE_CASE_CODE] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
One of our jobs was deadlocking with user routines, so we put the following code in the job step, just before the procedure is called:
DECLARE #deadlock_var NCHAR(3);
SET #deadlock_var = N'LOW';
SET DEADLOCK_PRIORITY #deadlock_var;
*--Call procedure
exec Client_myDeliveries_I_S*
However, the procedure is still in the stack that is processed.
Does the deadlock priority not get inherited through the chain of sub procedures?
I can confirm that at no point does the deadlock victim session set its deadlock_priority.
The complete deadlock XML is below, with some deletions for privacy:
<deadlock>
<victim-list>
<victimProcess id="process28fc21868" />
</victim-list>
<process-list>
<process id="process28fc21868" taskpriority="0" logused="2504" waitresource="KEY: 5:72057594562412544 (40fd182c0dd9)" waittime="5008" ownerId="299034576" transactionname="user_transaction" lasttranstarted="2018-02-01T12:22:55.580" XDES="0x140b2cc70" lockMode="X" schedulerid="1" kpid="3600" status="suspended" spid="87" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2018-02-01T12:22:55.580" lastbatchcompleted="2018-02-01T12:22:55.580" lastattention="2018-02-01T12:22:52.480" clientapp="EUROSTOP e-i Service" hostname="SRVAZBRWSQL01" hostpid="1328" loginname="sa" isolationlevel="read committed (2)" xactid="299034576" currentdb="5" lockTimeout="4294967295" clientoption1="673316896" clientoption2="128056">
<executionStack>
<frame procname="ALL.dbo.trgi_u_Constants" line="705" stmtstart="37030" stmtend="37270" sqlhandle="0x03000500651fce5aad8fd3002aa5000000000000000000000000000000000000000000000000000000000000">
update myconstants
set value = (select last_c_no from inserted)
where batch = 'last_c_no' </frame>
<frame procname="IF_TEST.myschema.SendTestData_Customers_SToALL_Customers" line="87" stmtstart="11420" stmtend="11650" sqlhandle="0x030007007f19ab663ac3e5005ca7000001000000000000000000000000000000000000000000000000000000">
update myschema.ALL_Constants
set last_c_no = #nLastCNumber + #NumberOfInsertedCs </frame>
<frame procname="IF_TEST.myschema.SendTestData_Customers" line="22" stmtstart="962" stmtend="1196" sqlhandle="0x030007009c954b7757b4d00054a7000001000000000000000000000000000000000000000000000000000000">
exec myschema.SendTestData_Customers_SToALL_Customers #MessageCode, #RejectAllOnValidationError </frame>
<frame procname="IF_TEST.myschema.SendSubmittedFData" line="13" stmtstart="876" stmtend="1114" sqlhandle="0x030007009b4fb66e2db4d00054a7000001000000000000000000000000000000000000000000000000000000">
exec [myschema].[SendTestData_Customers] #MessageCode, #RejectAllOnValidationError
-- Customer Orders </frame>
<frame procname="adhoc" line="1" stmtstart="104" sqlhandle="0x010007008547740e50dc4dd90700000000000000000000000000000000000000000000000000000000000000">
Exec myschema.SendSubmittedFData #0, #1, #2, #3 </frame>
<frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
(#0 nvarchar(4000),#1 int,#2 nvarchar(4000),#3 int);Exec myschema.SendSubmittedFData #0, #1, #2, #3 </inputbuf>
</process>
<process id="process2b6e94cf8" taskpriority="-6" logused="43652" waitresource="KEY: 5:72057594562412544 (d08358b1108f)" waittime="5009" ownerId="299033786" transactionname="user_transaction" lasttranstarted="2018-02-01T12:22:53.810" XDES="0x28262f130" lockMode="X" schedulerid="2" kpid="13408" status="suspended" spid="122" sbid="0" ecid="0" priority="-5" trancount="3" lastbatchstarted="2018-02-01T12:15:00.580" lastbatchcompleted="2018-02-01T12:15:00.580" lastattention="1900-01-01T00:00:00.580" clientapp="SQLAgent - TSQL JobStep (Job 0x24121E41ABD80643985B522FE6C248A7 : Step 1)" hostname="SRVAZBRWSQL01" hostpid="2252" loginname="SRVAZBRWSQL01\ALLSYSTEM" isolationlevel="read committed (2)" xactid="299033786" currentdb="5" lockTimeout="4294967295" clientoption1="673316896" clientoption2="128056">
<executionStack>
<frame procname="ALL.dbo.trgi_u_Constants" line="726" stmtstart="38142" stmtend="38386" sqlhandle="0x03000500651fce5aad8fd3002aa5000000000000000000000000000000000000000000000000000000000000">
update myconstants
set value = (select last_gs_rec_no from inserted)
where batch = 'last_gs_rec_no' </frame>
<frame procname="ALL.dbo.trgi_i_i_I_deliveries" line="1005" stmtstart="69046" stmtend="69220" sqlhandle="0x030005006cd104389a850d00a1a6000000000000000000000000000000000000000000000000000000000000">
update dbo.Constants set last_gs_rec_no = last_gs_rec_no + #nmyDeliveriesCreatedCount </frame>
<frame procname="C_HELP.dbo.Client_myDeliveries_I_Std" line="86" stmtstart="7306" stmtend="8324" sqlhandle="0x030006000d09a1438efcba0074a8000001000000000000000000000000000000000000000000000000000000">
insert into [ALL].dbo.i_I_deliveries
--[columns]
select --[columns]
from #Client_deliveries_PO_stg stg </frame>
<frame procname="C_HELP.dbo.Client_myDeliveries_I_S" line="446" stmtstart="32832" stmtend="33032" sqlhandle="0x0300060028323b37f7a70101eca7000001000000000000000000000000000000000000000000000000000000">
exec dbo.Client_myDeliveries_I_Std #week_selector, #username, #factory_bin_location, #Parameter </frame>
<frame procname="adhoc" line="6" stmtstart="216" sqlhandle="0x02000000e49e3111676b7e3aec714d06946692f70e3a8a880000000000000000000000000000000000000000">
exec Client_myDeliveries_I_S </frame>
</executionStack>
<inputbuf>
DECLARE #deadlock_var NCHAR(3);
SET #deadlock_var = N'LOW';
SET DEADLOCK_PRIORITY #deadlock_var;
exec Client_myDeliveries_I_S </inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594562412544" dbid="5" objectname="ALL.dbo.myconstants" indexname="pk_myconstants" id="lockd5ad7b00" mode="RangeS-U" associatedObjectId="72057594562412544">
<owner-list>
<owner id="process2b6e94cf8" mode="RangeS-S" />
</owner-list>
<waiter-list>
<waiter id="process28fc21868" mode="X" requestType="convert" />
</waiter-list>
</keylock>
<keylock hobtid="72057594562412544" dbid="5" objectname="ALL.dbo.myconstants" indexname="pk_myconstants" id="lock9466df00" mode="RangeS-U" associatedObjectId="72057594562412544">
<owner-list>
<owner id="process28fc21868" mode="RangeS-S" />
</owner-list>
<waiter-list>
<waiter id="process2b6e94cf8" mode="X" requestType="convert" />
</waiter-list>
</keylock>
</resource-list>
</deadlock>
Additional
Version:
Microsoft SQL Server 2012 (SP3-CU2) (KB3137746) - 11.0.6523.0 (X64)
Mar 2 2016 21:29:16
Copyright (c) Microsoft Corporation
Standard Edition (64-bit) on Windows NT 6.3 (Build 9600: ) (Hypervisor)
From everyone's input, it seems that deadlock priority can be ignored by SQL Server under circumstances that have not been made clear by Microsoft (that we know of); the algorithms used to determine the victim are not understood or documented. If anyone can find solid information on this topic, please share.
I'm trying to determine how this deadlock occurred and what fix I need to do to prevent it from happening again.
I've attached deadlock graph image from SSMS, resolution provided by SSMS image is not good, I apologize.
What is going on here is 3 processes are locked in a cycle, all waiting for the lock on the primary key for the table SecurityObject to be released.
The primary key for this table is clustered and is a composite key containing four columns.
The statement that each process is running is shown below. It is a delete command, deleting all records from table that match on a single column. The column is a GUID identifier that is one of the four columns in the composite clustered primary key.
DELETE FROM SecurityObject WHERE col1 = #val1
Where col1 is one of the four columns in the primary key.
I'm struggling to understand is how this scenario could have happened? How can there be a deadlock scenario for a primary key lock?
Below is the deadlock xml graph:
<deadlock>
<victim-list>
<victimProcess id="processaeabf84108"/>
</victim-list>
<process-list>
<process id="processaeabf84108" taskpriority="0" logused="0" waitresource="KEY: 14:72057594041925632 (00f78314b62e)" waittime="1754" ownerId="6629325" transactionname="user_transaction" lasttranstarted="2017-08-04T15:16:55.747" XDES="0xaea526f498" lockMode="X" schedulerid="2" kpid="16620" status="suspended" spid="73" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-08-04T15:16:55.747" lastbatchcompleted="2017-08-04T15:16:55.747" lastattention="1900-01-01T00:00:00.747" clientapp=".Net SqlClient Data Provider" hostname="RDXP0165C9JAWIE" hostpid="19084" loginname="REDMOND\RDXP0165C9JAWIE$" isolationlevel="read committed (2)" xactid="6629325" currentdb="14" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="SecurityAuthorization.DB.dbo.spDeleteAllSecurityObjects" line="5" stmtstart="342" stmtend="474" sqlhandle="0x03000e00b56a9938f8fcba00c3a7000001000000000000000000000000000000000000000000000000000000"> DELETE FROM [SecurityObject] WHERE [EnvironmentId] = #EnvironmentI </frame>
</executionStack>
<inputbuf> Proc [Database Id = 14 Object Id = 949578421] </inputbuf>
</process>
<process id="processaea64a9468" taskpriority="0" logused="0" waitresource="KEY: 14:72057594041925632 (e0caa7da41f0)" waittime="3981" ownerId="6629329" transactionname="user_transaction" lasttranstarted="2017-08-04T15:16:55.750" XDES="0xaea9602408" lockMode="X" schedulerid="1" kpid="14152" status="suspended" spid="76" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-08-04T15:16:55.750" lastbatchcompleted="2017-08-04T15:16:55.750" lastattention="1900-01-01T00:00:00.750" clientapp=".Net SqlClient Data Provider" hostname="RDXP0165C9JAWIE" hostpid="19084" loginname="REDMOND\RDXP0165C9JAWIE$" isolationlevel="read committed (2)" xactid="6629329" currentdb="14" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="SecurityAuthorization.DB.dbo.spDeleteAllSecurityObjects" line="5" stmtstart="342" stmtend="474" sqlhandle="0x03000e00b56a9938f8fcba00c3a7000001000000000000000000000000000000000000000000000000000000"> DELETE FROM [SecurityObject] WHERE [EnvironmentId] = #EnvironmentI </frame>
</executionStack>
<inputbuf> Proc [Database Id = 14 Object Id = 949578421] </inputbuf>
</process>
<process id="processaea686fc28" taskpriority="0" logused="884" waitresource="KEY: 14:72057594041925632 (e0caa7da41f0)" waittime="2105" ownerId="6638253" transactionname="user_transaction" lasttranstarted="2017-08-04T15:16:57.627" XDES="0xaea9460e58" lockMode="X" schedulerid="2" kpid="6528" status="suspended" spid="79" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-08-04T15:16:57.627" lastbatchcompleted="2017-08-04T15:16:57.627" lastattention="1900-01-01T00:00:00.627" clientapp=".Net SqlClient Data Provider" hostname="RDXP0165C9JAWIE" hostpid="19084" loginname="REDMOND\RDXP0165C9JAWIE$" isolationlevel="read committed (2)" xactid="6638253" currentdb="14" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="SecurityAuthorization.DB.dbo.spDeleteAllSecurityObjects" line="5" stmtstart="342" stmtend="474" sqlhandle="0x03000e00b56a9938f8fcba00c3a7000001000000000000000000000000000000000000000000000000000000"> DELETE FROM [SecurityObject] WHERE [EnvironmentId] = #EnvironmentI </frame>
</executionStack>
<inputbuf> Proc [Database Id = 14 Object Id = 949578421] </inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594041925632" dbid="14" objectname="SecurityAuthorization.DB.dbo.SecurityObject" indexname="PK__Security__185B78FE57F79F91" id="lockaead1a0680" mode="X" associatedObjectId="72057594041925632">
<owner-list>
<owner id="processaea686fc28" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="processaeabf84108" mode="X" requestType="wait"/>
</waiter-list>
</keylock>
<keylock hobtid="72057594041925632" dbid="14" objectname="SecurityAuthorization.DB.dbo.SecurityObject" indexname="PK__Security__185B78FE57F79F91" id="lockae6d468f80" mode="X" associatedObjectId="72057594041925632">
<owner-list>
<owner id="processaeabf84108" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="processaea64a9468" mode="X" requestType="wait"/>
</waiter-list>
</keylock>
<keylock hobtid="72057594041925632" dbid="14" objectname="SecurityAuthorization.DB.dbo.SecurityObject" indexname="PK__Security__185B78FE57F79F91" id="lockae6d468f80" mode="X" associatedObjectId="72057594041925632">
<owner-list>
<owner id="processaea64a9468" mode="X" requestType="wait"/>
</owner-list>
<waiter-list>
<waiter id="processaea686fc28" mode="X" requestType="wait"/>
</waiter-list>
</keylock>
</resource-list>
</deadlock>
Here is execution plan of stored procedure:
After further testing, I've managed to isolate the root cause of deadlock scenario to be concurrent calls to both DeleteAll (delete subset of records in table) and Insert (inserts a record that matches the criteria of the DeleteAll).
The exact sequence of events that leads to the deadlock scenario remains unclear, but the issue is solved by setting isolation level to serializable.
It is an acceptable side effect that it will hurt performance (for my scenario, we do not care about this operations performance because these operations are not being waited on, it is a fire and forget process).
I've strange deadlock graph where the victim chosen by MSSQL server 2008 isn't part of the deadlock loop.
This deadlock is between select and insert.
The deadlock resource is a single table where all selects want waitresource = "KEY: 6:72057594098810880 (ffffffffffff)"
Question1 : Does ffffffffffff here mean they want a full range lock on whole table? Or whole key range? Or something else?
We are following a rule where table will never have a row with primary key id = 0.
There are few places where we do this kind of check
select foo from bar where #someId = 0 OR SomeId = #someId.
I also came to know about that SQL doesn't short-circuit the expression. So if I pass #someId = 0 that doesn't guarantee the other part won't be evaluated. So it's possible that SQL can execute at runtime SomeId = #someId.
Question2 : Since it wasn't able to find 0 in SomeId, SQL will acquire a range lock on whole table (or row) so no one else inserts 0 id. Right?
With that assumption in mind I changed the where clause to this
(CASE
WHEN #someId = 0 THEN 1
WHEN SomeId = #someId THEN 1
ELSE 0
END = 1)
hoping that this will force evaluation order. But I'm wrong. I'm getting the deadlock again.
I've attached the deadlock graph below. I've renamed tables and sprocs involved (Company policy)
Question3 : Do you know what I'm missing here?
<deadlock-list>
<deadlock victim="process722c508">
<process-list>
<process id="process722c508" taskpriority="0" logused="0" waitresource="KEY: 6:72057594098810880 (ffffffffffff)" waittime="6217" ownerId="24219001" transactionname="SELECT" lasttranstarted="2011-05-17T03:29:16.033" XDES="0x80073a40" lockMode="RangeS-S" schedulerid="13" kpid="20436" status="suspended" spid="91" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2011-05-17T03:29:16.033" lastbatchcompleted="2011-05-17T03:29:16.033" clientapp=".Net SqlClient Data Provider" hostname="SOMEHOST" hostpid="28820" loginname="someloginname" isolationlevel="serializable (4)" xactid="24219001" currentdb="6" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
<executionStack>
<frame procname="fnGetTableResultAByBId" line="44" stmtstart="2246" stmtend="3566" sqlhandle="0x03000600800d7f0bda124000d99e00000000000000000000">
INSERT INTO #ReturnTable
SELECT Foo, Bar
FROM TheOneTable
WHERE ZId = #zId
AND (CASE
WHEN #yId = 0 THEN 1
WHEN YId = #yId THEN 1
ELSE 0
END = 1)
AND (CASE
WHEN #xId = 0 THEN 1
WHEN XId = #xId THEN 1
ELSE 0
END = 1) </frame>
<frame procname="GetViewCByDId" line="9" stmtstart="272" stmtend="2984" sqlhandle="0x03000600c21629025d8f3f00d99e00000100000000000000">
</frame>
</executionStack>
<inputbuf>
Proc [Database Id = 6 Object Id = 36247234] </inputbuf>
</process>
<process id="process7185048" taskpriority="0" logused="0" waitresource="KEY: 6:72057594098810880 (ffffffffffff)" waittime="6217" ownerId="24218992" transactionname="SELECT" lasttranstarted="2011-05-17T03:29:16.030" XDES="0x179980430" lockMode="RangeS-S" schedulerid="13" kpid="30616" status="suspended" spid="79" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2011-05-17T03:29:16.030" lastbatchcompleted="2011-05-17T03:29:16.030" clientapp=".Net SqlClient Data Provider" hostname="SOMEHOST" hostpid="28820" loginname="someloginname" isolationlevel="serializable (4)" xactid="24218992" currentdb="6" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
<executionStack>
<frame procname="fnGetTableResultAByBId" line="44" stmtstart="2246" stmtend="3566" sqlhandle="0x03000600800d7f0bda124000d99e00000000000000000000">
INSERT INTO #ReturnTable
SELECT Foo, Bar
FROM TheOneTable
WHERE ZId = #zId
AND (CASE
WHEN #yId = 0 THEN 1
WHEN YId = #yId THEN 1
ELSE 0
END = 1)
AND (CASE
WHEN #xId = 0 THEN 1
WHEN XId = #xId THEN 1
ELSE 0
END = 1) </frame>
<frame procname="GetViewCByDId" line="9" stmtstart="272" stmtend="2984" sqlhandle="0x03000600c21629025d8f3f00d99e00000100000000000000">
</frame>
</executionStack>
<inputbuf>
Proc [Database Id = 6 Object Id = 36247234] </inputbuf>
</process>
<process id="process7223048" taskpriority="0" logused="0" waitresource="KEY: 6:72057594098810880 (ffffffffffff)" waittime="5330" ownerId="24235090" transactionname="SELECT" lasttranstarted="2011-05-17T03:29:16.927" XDES="0x840d3b30" lockMode="RangeS-S" schedulerid="15" kpid="23452" status="suspended" spid="88" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2011-05-17T03:29:16.927" lastbatchcompleted="2011-05-17T03:29:16.927" clientapp=".Net SqlClient Data Provider" hostname="SOMEHOST" hostpid="28820" loginname="someloginname" isolationlevel="serializable (4)" xactid="24235090" currentdb="6" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
<executionStack>
<frame procname="GetOneRowEByFId" line="11" stmtstart="260" stmtend="2456" sqlhandle="0x03000600db082c08ba823f00d99e00000100000000000000">
SELECT TOP 1
Col1, Col2, Col3
FROM The2ndTable
INNER JOIN [dbo].[TheOneTable] ON [dbo].[TheOneTable].[LinkBetweenOneAndTwoId]=[The2ndTable].[LinkBetweenOneAndTwoId]
WHERE [dbo].[TheOneTable].ZId= #ActivityId and
[TheOneTable].[n
</frame>
</executionStack>
<inputbuf>
Proc [Database Id = 6 Object Id = 137103579] </inputbuf>
</process>
<process id="process6334088" taskpriority="0" logused="0" waitresource="KEY: 6:72057594098810880 (ffffffffffff)" waittime="5668" ownerId="24229434" transactionname="SELECT" lasttranstarted="2011-05-17T03:29:16.587" XDES="0x17ea9ac90" lockMode="RangeS-S" schedulerid="12" kpid="5104" status="suspended" spid="86" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2011-05-17T03:29:16.587" lastbatchcompleted="2011-05-17T03:29:16.587" clientapp=".Net SqlClient Data Provider" hostname="SOMEHOST" hostpid="28820" loginname="someloginname" isolationlevel="serializable (4)" xactid="24229434" currentdb="6" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
<executionStack>
<frame procname="GetOneRowEByFId" line="11" stmtstart="260" stmtend="2456" sqlhandle="0x03000600db082c08ba823f00d99e00000100000000000000">
SELECT TOP 1
Col1, Col2, Col3
FROM The2ndTable
INNER JOIN [dbo].[TheOneTable] ON [dbo].[TheOneTable].[LinkBetweenOneAndTwoId]=[The2ndTable].[LinkBetweenOneAndTwoId]
WHERE [dbo].[TheOneTable].ZId= #ActivityId and
[TheOneTable].[n</frame>
</executionStack>
<inputbuf>
Proc [Database Id = 6 Object Id = 137103579] </inputbuf>
</process>
<process id="process8808e08" taskpriority="0" logused="0" waitresource="KEY: 6:72057594098810880 (ffffffffffff)" waittime="6652" ownerId="24217112" transactionname="SELECT" lasttranstarted="2011-05-17T03:29:15.610" XDES="0x833b5ca0" lockMode="RangeS-S" schedulerid="1" kpid="19752" status="suspended" spid="89" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2011-05-17T03:29:15.610" lastbatchcompleted="2011-05-17T03:29:15.610" clientapp=".Net SqlClient Data Provider" hostname="SOMEHOST" hostpid="28820" loginname="someloginname" isolationlevel="serializable (4)" xactid="24217112" currentdb="6" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
<executionStack>
<frame procname="GetOneRowEByFId" line="11" stmtstart="260" stmtend="2456" sqlhandle="0x03000600db082c08ba823f00d99e00000100000000000000">
SELECT TOP 1
Col1, Col2, Col3
FROM The2ndTable
INNER JOIN [dbo].[TheOneTable] ON [dbo].[TheOneTable].[LinkBetweenOneAndTwoId]=[The2ndTable].[LinkBetweenOneAndTwoId]
WHERE [dbo].[TheOneTable].ZId= #ActivityId and
[TheOneTable].[n
</frame>
</executionStack>
<inputbuf>
Proc [Database Id = 6 Object Id = 137103579] </inputbuf>
</process>
<process id="process5c08988" taskpriority="0" logused="1644" waitresource="KEY: 6:72057594098810880 (91a0638558d2)" waittime="4889" ownerId="24214248" transactionname="user_transaction" lasttranstarted="2011-05-17T03:29:15.327" XDES="0x186609470" lockMode="RangeI-N" schedulerid="9" kpid="9000" status="suspended" spid="102" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2011-05-17T03:29:15.330" lastbatchcompleted="2011-05-17T03:29:15.330" clientapp=".Net SqlClient Data Provider" hostname="SOMEHOST" hostpid="28820" loginname="someloginname" isolationlevel="serializable (4)" xactid="24214248" currentdb="6" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
<executionStack>
<frame procname="InsertIntoTheOneTable" line="25" stmtstart="1334" stmtend="2608" sqlhandle="0x03000600bbbacb5d25883f00d99e00000100000000000000">
INSERT INTO [dbo].[TheOneTable] (Some,Col,Here)
VALUES (#some,#col,#here) </frame>
</executionStack>
<inputbuf>
Proc [Database Id = 6 Object Id = 1573632699] </inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594098810880" dbid="6" objectname="TheOneTable" indexname="PK_TheOneTable" id="lock6b17a00" mode="RangeI-N" associatedObjectId="72057594098810880">
<owner-list />
<waiter-list>
<waiter id="process722c508" mode="RangeS-S" requestType="wait" />
</waiter-list>
</keylock>
<keylock hobtid="72057594098810880" dbid="6" objectname="TheOneTable" indexname="PK_TheOneTable" id="lock6b17a00" mode="RangeI-N" associatedObjectId="72057594098810880">
<owner-list />
<waiter-list>
<waiter id="process7185048" mode="RangeS-S" requestType="wait" />
</waiter-list>
</keylock>
<keylock hobtid="72057594098810880" dbid="6" objectname="TheOneTable" indexname="PK_TheOneTable" id="lock6b17a00" mode="RangeI-N" associatedObjectId="72057594098810880">
<owner-list />
<waiter-list>
<waiter id="process7223048" mode="RangeS-S" requestType="wait" />
</waiter-list>
</keylock>
<keylock hobtid="72057594098810880" dbid="6" objectname="TheOneTable" indexname="PK_TheOneTable" id="lock6b17a00" mode="RangeI-N" associatedObjectId="72057594098810880">
<owner-list />
<waiter-list>
<waiter id="process6334088" mode="RangeS-S" requestType="wait" />
</waiter-list>
</keylock>
<keylock hobtid="72057594098810880" dbid="6" objectname="TheOneTable" indexname="PK_TheOneTable" id="lock6b17a00" mode="RangeI-N" associatedObjectId="72057594098810880">
<owner-list>
<owner id="process5c08988" mode="RangeI-N" />
</owner-list>
<waiter-list>
<waiter id="process8808e08" mode="RangeS-S" requestType="wait" />
</waiter-list>
</keylock>
<keylock hobtid="72057594098810880" dbid="6" objectname="TheOneTable" indexname="PK_TheOneTable" id="lock6372e80" mode="RangeS-S" associatedObjectId="72057594098810880">
<owner-list>
<owner id="process7223048" mode="RangeS-S" />
<owner id="process6334088" mode="RangeS-S" />
</owner-list>
<waiter-list>
<waiter id="process5c08988" mode="RangeI-N" requestType="wait" />
</waiter-list>
</keylock>
</resource-list>
</deadlock>
</deadlock-list>
In the context of locking, tables and their related indexes are separate entities. At times, dead locking happens between a table and its index, rather than between two separate tables.
The problem is most likely when a lock is aquired on an index and then another lock is aquired on the related table (i.e. bar) to do the data lookup. During the insert, this will happen in the opposite order. First, the table (i.e. bar) is locked and updated, then indexes are locked.
select foo
from bar
where #someId = 0 OR SomeId = #someId
Do you have/can you add a covering index (to help with the select) that contains both the SomeId and foo ? This way you'll avoid the lookup altogether and stop the problem from occuring.
Can you post the query plans rather than deadlock frames?
Would you mind trying some alternate criteria? I've been playing with this method lately (only I use NULLs not 0 to mean all values):
SET #yId = NullIf(#yId, 0);
SET #xId = NullIf(#xId, 0);
...
WHERE
#yId BETWEEN Coalesce(#yId, 0) AND Coalesce(#yId, 2147483647)
AND #xId BETWEEN Coalesce(#xId, 0) AND Coalesce(#xId, 2147483647)
Or you could use your zeroes intact:
WHERE
#yId BETWEEN #yId AND Coalesce(NullIf(#yId, 0), 2147483647)
AND #xId BETWEEN #xId AND Coalesce(NullIf(#xId, 0), 2147483647)
Thinking about this a little more... just to review, deadlocks only occur because of conflicting resource acquisition order. A resource is not just a table but rows, extents, pages, etc. If two queries are being submitted at once that initially acquire a smaller granularity lock, then escalate their locks to a something that overlaps the smaller lock the other process has, then you get a deadlock.
So, is there any way you can either acquire the larger lock earlier, avoid acquiring the larger conflicting lock, or change the resource acquisition order?
You could experiment with using WITH (TABLOCKX) which sounds horrible, but if your #yId or #xId is 0 thus making you select all rows, you're going to need the whole table anyway.
Have you also considered trying OPTION (MAXDOP 1) just to see if it helps? Theoretically, having multiple streams for the same data request could increase the likelihood of conflicting locks being acquired simultaneously.
Does the table have a clustered index? If not, add it, and if so, is it being used or can you force it to be used? This potentially could make the queries access the table in a different way, preventing the deadlock.
Post your comments and I'll see if any more ideas come up based on your responses.