Dead locking when accessing the same table - sql

I have a C# application that works with data in a SQL Server table via stored procedures.
If 2 users execute a form copy at the same time the same procedure is executed where dead locking occurs
I got xml_deadlock_report in SQL Server under system_health. Here is what it looks like:
<deadlock>
<victim-list>
<victimProcess id="process10f2ea0188" />
</victim-list>
<process-list>
<process id="process10f2ea0188" taskpriority="0" logused="7424" waitresource="PAGE: 5:1:24768207 " waittime="83419" ownerId="61921962" transactionname="user_transaction" lasttranstarted="2022-06-21T05:40:40.083" XDES="0x6d4f80d28" lockMode="S" schedulerid="5" kpid="2392" status="suspended" spid="69" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2022-06-21T05:40:40.143" lastbatchcompleted="2022-06-21T05:40:40.083" lastattention="1900-01-01T00:00:00.083" clientapp=".Net SqlClient Data Provider" hostname="GKA" hostpid="12432" loginname="admin" isolationlevel="read committed (2)" xactid="61921962" currentdb="5" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128048">
<executionStack>
<frame procname="CreateSectionTable" line="28" stmtstart="2856" stmtend="2984" sqlhandle="0x030005005611575922a96e01ccad000001000000000000000000000000000000000000000000000000000000">
SELECT #nTableIndex = Max(DataTableIndex) + 1 FROM SectionTable </frame>
<frame procname="CopySectionInt" line="118" stmtstart="9900" stmtend="10098" sqlhandle="0x030005001e33f860a2a96e01ccad000001000000000000000000000000000000000000000000000000000000">
EXEC CreateSectionTable #SectionId, '', '', ''
/* копирование в него структуры столбцов */ </frame>
<frame procname="CopyForm" line="73" stmtstart="6408" stmtend="6530" sqlhandle="0x03000500acea0f5f7ea96e01ccad000001000000000000000000000000000000000000000000000000000000">
EXEC CopySectionInt #SectionID, #FormId, null, null </frame>
</executionStack>
<inputbuf>
Proc [Database Id = 5 Object Id = 1594878636] </inputbuf>
</process>
<process id="process10ff03d498" taskpriority="0" logused="26216496" waitresource="PAGE: 5:1:23196243 " waittime="3894" ownerId="61920617" transactionname="user_transaction" lasttranstarted="2022-06-21T05:40:34.813" XDES="0x58a276d28" lockMode="S" schedulerid="6" kpid="4420" status="suspended" spid="66" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2022-06-21T05:40:34.817" lastbatchcompleted="2022-06-21T05:40:34.813" lastattention="1900-01-01T00:00:00.813" clientapp=".Net SqlClient Data Provider" hostname="SQLSER" hostpid="6120" loginname="SS\Bo" isolationlevel="read committed (2)" xactid="61920617" currentdb="5" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128048">
<executionStack>
<frame procname="CreateSectionTable" line="28" stmtstart="2856" stmtend="2984" sqlhandle="0x030005005611575922a96e01ccad000001000000000000000000000000000000000000000000000000000000">
SELECT #nTableIndex = Max(DataTableIndex) + 1 FROM SectionTable </frame>
<frame procname="CopySectionInt" line="118" stmtstart="9900" stmtend="10098" sqlhandle="0x030005001e33f860a2a96e01ccad000001000000000000000000000000000000000000000000000000000000">
EXEC CreateSectionTable #SectionId, '', '', ''
/* копирование в него структуры столбцов */ </frame>
<frame procname="CopyForm" line="73" stmtstart="6408" stmtend="6530" sqlhandle="0x03000500acea0f5f7ea96e01ccad000001000000000000000000000000000000000000000000000000000000">
EXEC CopySectionInt #SectionID, #FormId, null, null </frame>
</executionStack>
<inputbuf>
Proc [Database Id = 5 Object Id = 1594878636] </inputbuf>
</process>
</process-list>
<resource-list>
<pagelock fileid="1" pageid="24768207" dbid="5" subresource="FULL" objectname="SectionTable" id="locke19f0d280" mode="SIX" associatedObjectId="72057612168593408">
<owner-list>
<owner id="process10ff03d498" mode="SIX" />
</owner-list>
<waiter-list>
<waiter id="process10f2ea0188" mode="S" requestType="wait" />
</waiter-list>
</pagelock>
<pagelock fileid="1" pageid="23196243" dbid="5" subresource="FULL" objectname="SectionTable" id="locke4b2da480" mode="SIX" associatedObjectId="72057612168593408">
<owner-list>
<owner id="process10f2ea0188" mode="SIX" />
</owner-list>
<waiter-list>
<waiter id="process10ff03d498" mode="S" requestType="wait" />
</waiter-list>
</pagelock>
</resource-list>
</deadlock>
This is what the form copying procedure itself looks like (all the inputs, except the first parameter, are the same for the two processes.
For example, process1 = CopyForm (12345, 12, 35), process2 = CopyForm (54321, 12, 35)):
Here is the code itself with the procedures:
CREATE PROCEDURE [dbo].[CopyForm](#FormId as uniqueidentifier, #SrcFormId as uniqueidentifier, #DestFormTypeId as uniqueidentifier)
AS
BEGIN
/* Here we declare the variables */
SET NOCOUNT ON
SET ANSI_WARNINGS OFF
/*code to create a new form */
DECLARE cr CURSOR LOCAL FAST_FORWARD FOR
SELECT ObjectID FROM SectionTable
WHERE Form = #SrcFormId
ORDER BY OrderNum desc
OPEN cr
FETCH NEXT FROM cr INTO #SectionID
WHILE ##FETCH_STATUS = 0 BEGIN
EXEC CopySectionInt #SectionID, #FormId, null, null
FETCH NEXT FROM cr INTO #SectionID
END
CLOSE cr
DEALLOCATE cr
END
GO
CREATE PROCEDURE [dbo].[CopySectionInt](
#SrcSectionId as uniqueidentifier,
#DestFormId as uniqueidentifier,
#PrevSectId as uniqueidentifier,
#SectionId uniqueidentifier )
AS
BEGIN
/* Here we declare the variables */
SET NOCOUNT ON
IF #SectionId IS NULL SELECT #SectionId = newid()
IF #PrevSectId IS NULL
SET #nSectionNumber = 1
ELSE
SELECT #nSectionNumber = OrderNum + 1 FROM SectionTable WHERE ObjectID = #PrevSectId
/* Here we create a new section */
EXEC CreateSectionTable #SectionId
/* remaining code */
END
GO
CREATE PROCEDURE [dbo].[CreateSectionTable]( #argObjectID uniqueidentifier )
AS
BEGIN
/* Here we declare the variables */
SELECT #nSectionNumber = OrderNum FROM SectionTable WHERE ObjectID = #argObjectID
SELECT #nTableIndex = Max(DataTableIndex) + 1 FROM SectionTable
SET #nTableIndex = ISNULL( #nTableIndex, 1 )
UPDATE SectionTable SET DataTableIndex = #nTableIndex, OrderNum = #nSectionNumber WHERE ObjectID = #argObjectID
END
GO
I don't understand how these two processes are blocking each other if they are both accessing the same resource? And how can I fix this error?
Would blocking the CopyForm procedure with sp_getapplock help me in this case ? Or is there a way to solve the problem without blocking?

Related

SQL deadlock on concurrent delete

I have an API endpoint to delete children of a parent, unless a child is specified. The SQL is:
DELETE FROM
Child
WHERE
ParentID = #ParentID AND
ChildID NOT IN (#ChildIDToKeep);
(This query is sanitised, so ignore the fact that I'm not doing ChildID != #ChildIDToKeep)
The executed SQL is also wrapped in a transaction.
My web application attempts to send hundreds of requests to this endpoint concurrently, so this SQL transaction gets executed concurrently many times. For some reason this is causing a deadlock.
Why am I getting a deadlock, and what can I do to resolve it?
Here is the deadlock XML:
<deadlock>
<victim-list>
<victimProcess id="process25c3045b848" />
</victim-list>
<process-list>
<process id="process25c3045b848" taskpriority="0" logused="6336" waitresource="OBJECT: 14:788354023:0 " waittime="3039" ownerId="39730548" transactionname="user_transaction" lasttranstarted="2021-08-13T13:28:05.690" XDES="0x25c36f9f900" lockMode="IX" schedulerid="1" kpid="6588" status="suspended" spid="70" sbid="4" ecid="0" priority="0" trancount="2" lastbatchstarted="2021-08-13T13:28:05.957" lastbatchcompleted="2021-08-13T13:28:05.957" lastattention="1900-01-01T00:00:00.957" clientapp="Core .Net SqlClient Data Provider" hostname="DER-UK-LAP-101" hostpid="14344" loginname="USERNAME" isolationlevel="serializable (4)" xactid="39730548" currentdb="14" currentdbname="Database" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="adhoc" line="2" stmtstart="102" stmtend="500" sqlhandle="0x0200000053d2042c7d4e4199da6b8531c53adb3464bc8b610000000000000000000000000000000000000000">
unknown </frame>
<frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
(#ParentID int,#ChildIDToKeep int)
DELETE FROM
Child
WHERE
ParentID = #ParentID AND
ChildID NOT IN (#ChildIDToKeep); </inputbuf>
</process>
<process id="process25c2a462ca8" taskpriority="0" logused="6348" waitresource="OBJECT: 14:788354023:0 " waittime="2901" ownerId="39730549" transactionname="user_transaction" lasttranstarted="2021-08-13T13:28:05.690" XDES="0x25c39560e80" lockMode="IX" schedulerid="2" kpid="2252" status="suspended" spid="63" sbid="2" ecid="0" priority="0" trancount="2" lastbatchstarted="2021-08-13T13:28:06.097" lastbatchcompleted="2021-08-13T13:28:06.097" lastattention="1900-01-01T00:00:00.097" clientapp="Core .Net SqlClient Data Provider" hostname="DER-UK-LAP-101" hostpid="14344" loginname="USERNAME" isolationlevel="serializable (4)" xactid="39730549" currentdb="14" currentdbname="Database" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="adhoc" line="2" stmtstart="102" stmtend="500" sqlhandle="0x0200000053d2042c7d4e4199da6b8531c53adb3464bc8b610000000000000000000000000000000000000000">
unknown </frame>
<frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
(#ParentID int,#ChildIDToKeep int)
DELETE FROM
Child
WHERE
ParentID = #ParentID AND
ChildID NOT IN (#ChildIDToKeep); </inputbuf>
</process>
</process-list>
<resource-list>
<objectlock lockPartition="0" objid="788354023" subresource="FULL" dbid="14" objectname="Database.dbo.Child" id="lock255443f2580" mode="S" associatedObjectId="788354023">
<owner-list>
<owner id="process25c2a462ca8" mode="S" />
<owner id="process25c2a462ca8" mode="IX" requestType="convert" />
</owner-list>
<waiter-list>
<waiter id="process25c3045b848" mode="IX" requestType="convert" />
</waiter-list>
</objectlock>
<objectlock lockPartition="0" objid="788354023" subresource="FULL" dbid="14" objectname="Database.dbo.Child" id="lock255443f2580" mode="S" associatedObjectId="788354023">
<owner-list>
<owner id="process25c3045b848" mode="S" />
<owner id="process25c3045b848" mode="IX" requestType="convert" />
</owner-list>
<waiter-list>
<waiter id="process25c2a462ca8" mode="IX" requestType="convert" />
</waiter-list>
</objectlock>
</resource-list>
</deadlock>
Here is the deadlock graph:
You've asked for the deadlock by running the transaction in SERIALIZABLE isolation level:
isolationlevel="serializable (4)"
SERIALIZABLE transactions hold shared (S) locks until the end of the transaction, so when you "Select Parent's Children." you acquire and hold a S lock. And it's a table lock since you don't have proper indexing on the table.
SERIALIZABLE enforces its guarantees with deadlocks, see rant here: using new TransactionScope() considered harmful.
So when two sessions take and hold an S lock on the table, neither can acquire the IX lock required to start the DELETE, nor would they be able to acquire the X lock required to complete the DELETE. SERIALIZABLE is just doing its job here, as each session has read data that the other is trying to update, so if allowed to complete, the state of the database would not be the same as if the transactions ran sequentially, which is the definition of SERIALIZABLE. So it's expected one of the transactions must be terminated, and that you would catch and retry if you still needed that work done.
So I would drop to READ_COMMITTED, and put an index on ParentId. You might want to put an UPDLOCK hint on your query to "Select Parent" so two sessions can't run against the same parent concurrently.

Why SELECT shows Update lock in deadlock graph

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).

Setting deadlock_priority does not make LOW priority session the victim

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.

Deadlock puzzle : Victim doesn't own any resource, used to kill to resolve deadlock

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.

SQL server update statements cause deadlocks

I have an application which is running multiple sql statements simultaneously in different threads, causing various deadlocks which all seem to come from one table. For example the two update statements below:
UPDATE WF SET QUEUETIME='2011-02-18 13:06:53.578', STATE = 'outbound', USER = '', TIME = null
WHERE PID = 'MessageProcessing' AND ACTIVITYID = 'Delete' AND ITEMID = '120' AND TRANID = 'Created' AND STATE = 'ready' AND USER = ''
UPDATE WF SET QUEUETIME='2011-02-18 13:06:53.625', STATE = 'ready', USER = '', TIME = null
WHERE PID = 'standardOutbound' AND ACTIVITYID = 'Node1' AND ITEMID = '121' AND TRANID = 'toNode1' AND STATE = '' AND USER = ''
produce the following deadlock:
<deadlock-list>
<deadlock victim="process6d8e38">
<process-list>
<process id="process6d8e38" taskpriority="0" logused="272" waitresource="RID: 7:1:564:14" waittime="625" ownerId="430343" transactionname="implicit_transaction" lasttranstarted="2011-02-18T13:06:53.640" XDES="0xb44a258" lockMode="U" schedulerid="1" kpid="2632" status="suspended" spid="58" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2011-02-18T13:06:53.640" lastbatchcompleted="2011-02-18T13:06:53.640" clientapp="jTDS" hostname="INTERWOV-FP1" hostpid="123" loginname="database1" isolationlevel="read committed (2)" xactid="430343" currentdb="7" TIMEout="4294967295" clientoption1="671088672" clientoption2="128058">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="336" sqlhandle="0x0200000077e2b21749c20d3ca2ca8d4d89ea5ea29336e03e">
UPDATE WF SET QUEUETIME = #P0 , STATE = #P1 , USER = #P2 , TIME = #P3 WHERE PID = #P4 AND ACTIVITYID = #P5 AND ITEMID = #P6 AND TRANID = #P7 AND STATE = #P8 AND USER = #P9 </frame>
</executionStack>
<inputbuf>
(#P0 datetime,#P1 nvarchar(4000),#P2 nvarchar(4000),#P3 datetime,#P4 nvarchar(4000),#P5 nvarchar(4000),#P6 int,#P7 nvarchar(4000),#P8 nvarchar(4000),#P9 nvarchar(4000))UPDATE WF SET QUEUETIME = #P0 , STATE = #P1 , USER = #P2 , TIME = #P3 WHERE PID = #P4 AND ACTIVITYID = #P5 AND ITEMID = #P6 AND TRANID = #P7 AND STATE = #P8 AND USER = #P9 </inputbuf>
</process>
<process id="process8ccb68" taskpriority="0" logused="900" waitresource="RID: 7:1:564:12" waittime="625" ownerId="430341" transactionname="implicit_transaction" lasttranstarted="2011-02-18T13:06:53.623" XDES="0xaeccf48" lockMode="U" schedulerid="2" kpid="312" status="suspended" spid="53" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2011-02-18T13:06:53.640" lastbatchcompleted="2011-02-18T13:06:53.623" clientapp="jTDS" hostname="INTERWOV-FP1" hostpid="123" loginname="database1" isolationlevel="read committed (2)" xactid="430341" currentdb="7" TIMEout="4294967295" clientoption1="671088672" clientoption2="128058">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="336" sqlhandle="0x0200000077e2b21749c20d3ca2ca8d4d89ea5ea29336e03e">
UPDATE WF SET QUEUETIME = #P0 , STATE = #P1 , USER = #P2 , TIME = #P3 WHERE PID = #P4 AND ACTIVITYID = #P5 AND ITEMID = #P6 AND TRANID = #P7 AND STATE = #P8 AND USER = #P9 </frame>
</executionStack>
<inputbuf>
(#P0 datetime,#P1 nvarchar(4000),#P2 nvarchar(4000),#P3 datetime,#P4 nvarchar(4000),#P5 nvarchar(4000),#P6 int,#P7 nvarchar(4000),#P8 nvarchar(4000),#P9 nvarchar(4000))UPDATE WF SET QUEUETIME = #P0 , STATE = #P1 , USER = #P2 , TIME = #P3 WHERE PID = #P4 AND ACTIVITYID = #P5 AND ITEMID = #P6 AND TRANID = #P7 AND STATE = #P8 AND USER = #P9 </inputbuf>
</process>
</process-list>
<resource-list>
<ridlock fileid="1" pageid="564" dbid="7" objectname="database1.dbo.WF" id="lock3a63dc0" mode="X" associatedObjectId="72057594077577216">
<owner-list>
<owner id="process6d8e38" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="process8ccb68" mode="U" requestType="wait"/>
</waiter-list>
</ridlock>
<ridlock fileid="1" pageid="564" dbid="7" objectname="database1.dbo.WF" id="lock3a65f40" mode="X" associatedObjectId="72057594077577216">
<owner-list>
<owner id="process8ccb68" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="process6d8e38" mode="U" requestType="wait"/>
</waiter-list>
</ridlock>
</resource-list>
</deadlock>
</deadlock-list>
I realise some amount of deadlocks are inevitable and the application should handle them (which it does), but I don't understand why they should be happening in this case. In my simplistic mind these two statements should be locking different rows, and even if they were updating the same row, one should just wait for the other.
Can anyone explain why they are causing deadlocks, or give any suggestions as to how to prevent them?
We have 3 non-clustered indexes on the table (PID, ACTIVITYID, ITEMID, TRANID), (ITEMID), and (PID, ACTIVITYID). (PID, ACTIVITYID, ITEMID, TRANID) form the primary key. I've tried (somewhat blindly) playing about with the indexes, but seemingly to no avail.
The application is running on weblogic and sql server 2005, and I've reproduced the deadlocks on websphere and sql server 2008. The don't seem to occur when using an oracle database, but this is unfortunately not an option for our client!
Many thanks to anyone who can offer help or insight into this.
Problem are usually not updates alone, but combination of selects and updates. Consider scenario, where transaction selects some row and then updates it. If two such transactions run parallel, deadlock occurs.
Simplest solution is use UPDLOCK (and optionally ROWLOCK) hint in select statements; of course only for records, updated after - otherwise you may end in slow application.
Is there any clustered index? Any indexed views? Any other indexes? The columns being updated don't appear to be in any indexes. Row-level locking should be fine, but something must be causing an escalation. The sets appear to be disjoint, but perhaps the pages are overlapping (hence my question about the choice of clustered index).
http://msdn.microsoft.com/en-us/library/ms184286.aspx