SQL deadlock on concurrent delete - sql

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.

Related

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

Analysing sql deadlock xml

I need help in analysing the following deadlock xml
<deadlock>
<victim-list>
<victimProcess id="processa9f6f73c28" />
</victim-list>
<process-list>
<process id="processa9f6f73c28" taskpriority="0" logused="0" waitresource="KEY: 5:72057594060013568 (bd1a413b4dd8)" waittime="1759" ownerId="19463226" transactionname="user_transaction" lasttranstarted="2018-05-21T14:43:38.640" XDES="0xa9dec70458" lockMode="X" schedulerid="2" kpid="8068" status="suspended" spid="122" sbid="2" ecid="0" priority="0" trancount="2" lastbatchstarted="2018-05-21T14:43:38.640" lastbatchcompleted="2018-05-21T14:43:38.637" lastattention="1900-01-01T00:00:00.637" clientapp=".Net SqlClient Data Provider" hostname="RD0003FF430FC8" hostpid="12344" loginname="officearchitect" isolationlevel="read committed (2)" xactid="19463226" currentdb="5" currentdbname="OfficeArchitect_Performance_Test" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
<executionStack>
<frame procname="d2558974-73ab-4869-acd2-9cce4009286e.model.RelationshipPair_DeleteByRelationshipIds" queryhash="0x9a6597d902cb7ffa" queryplanhash="0x4f762f1ec930146f" line="7" stmtstart="302" stmtend="566" sqlhandle="0x03000500f540c416e4e82300e7a8000001000000000000000000000000000000000000000000000000000000">DELETE
RP
FROM
[model].RelationshipPair RP
INNER JOIN
#RelationshipIdTable RIT
ON
RP.RelationshipId = RIT.EntityI</frame>
<frame procname="d2558974-73ab-4869-acd2-9cce4009286e.model.ModelItem_Relationship_Delete" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="20" stmtstart="910" stmtend="1066" sqlhandle="0x030005000d989e702ae82300e7a8000001000000000000000000000000000000000000000000000000000000">EXEC [model].[RelationshipPair_DeleteByRelationshipIds]
#RelationshipIdTabl</frame>
</executionStack>
<inputbuf>Proc [Database Id = 5 Object Id = 1889441805]</inputbuf>
</process>
<process id="processa9f9857088" taskpriority="0" logused="624" waitresource="KEY: 5:72057594060013568 (3f1e49aa6519)" waittime="2779" ownerId="19414353" transactionname="user_transaction" lasttranstarted="2018-05-21T14:43:28.600" XDES="0xaa0a244458" lockMode="RangeS-S" schedulerid="2" kpid="51500" status="suspended" spid="164" sbid="2" ecid="0" priority="0" trancount="2" lastbatchstarted="2018-05-21T14:43:28.603" lastbatchcompleted="2018-05-21T14:43:28.593" lastattention="2018-05-21T14:38:44.820" clientapp=".Net SqlClient Data Provider" hostname="RD0003FF430FC8" hostpid="12344" loginname="officearchitect" isolationlevel="read committed (2)" xactid="19414353" currentdb="5" currentdbname="OfficeArchitect_Performance_Test" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
<executionStack>
<frame procname="d2558974-73ab-4869-acd2-9cce4009286e.model.ModelItem_Generic_Create" queryhash="0x21c1a974c29371a5" queryplanhash="0x60900e552e5614c5" line="17" stmtstart="898" stmtend="1402" sqlhandle="0x030005005dcdd742fde62300e7a8000001000000000000000000000000000000000000000000000000000000">INSERT INTO [model].[ModelItem]
(
[MetamodelItemId],
[ModelItemCategoryId]
)
OUTPUT [inserted].[ModelItemId], [inserted].[MetamodelItemId]
INTO #ModelItemIdsByMetamodelId
SELECT EntityId, #ModelItemCategoryId
FROM #MetamodelItemIdTabl</frame>
<frame procname="d2558974-73ab-4869-acd2-9cce4009286e.model.ModelItem_Relationship_Create" queryhash="0x41bf1ae3ccbfaccc" queryplanhash="0x76a3cb6aa572b737" line="134" stmtstart="9960" stmtend="10500" sqlhandle="0x030005009b4fb66e1be82300e7a8000001000000000000000000000000000000000000000000000000000000">INSERT INTO #tempStorage
EXECUTE [model].[ModelItem_Generic_Create]
#MetamodelItemIdTable = #metamodelIds,
#ModelId = #ModelId,
#ModelItemCategoryId = #ModelItemCategoryId,
#DateLastModified = #DateLastModified,
#LastModifiedBy = #LastModifiedB</frame>
</executionStack>
<inputbuf>Proc [Database Id = 5 Object Id = 1857441691]</inputbuf>
</process>
<process id="processa9fb862108" taskpriority="0" logused="43256" waitresource="KEY: 5:72057594060013568 (bd1a413b4dd8)" waittime="40" ownerId="19385479" transactionname="user_transaction" lasttranstarted="2018-05-21T14:43:27.370" XDES="0xa9da75c458" lockMode="RangeS-S" schedulerid="1" kpid="51692" status="suspended" spid="193" sbid="2" ecid="0" priority="0" trancount="2" lastbatchstarted="2018-05-21T14:43:40.320" lastbatchcompleted="2018-05-21T14:43:40.327" lastattention="1900-01-01T00:00:00.327" clientapp=".Net SqlClient Data Provider" hostname="RD0003FF430FC8" hostpid="12344" loginname="officearchitect" isolationlevel="read committed (2)" xactid="19385479" currentdb="5" currentdbname="OfficeArchitect_Performance_Test" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
<executionStack>
<frame procname="d2558974-73ab-4869-acd2-9cce4009286e.model.ModelItem_Generic_Delete" queryhash="0xd6e2f8f770b21179" queryplanhash="0x18df7aa720a890f6" line="80" stmtstart="4110" stmtend="4360" sqlhandle="0x0300050096f1cb4302e72300e7a8000001000000000000000000000000000000000000000000000000000000">DELETE
MI
FROM
[model].ModelItem MI
INNER JOIN
#ModelItemIdTable MIT
ON
MIT.EntityId = MI.ModelItemI</frame>
<frame procname="d2558974-73ab-4869-acd2-9cce4009286e.model.ModelItem_Object_Delete" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="25" stmtstart="1088" stmtend="1302" sqlhandle="0x0300050061e52c65bce72300e7a8000001000000000000000000000000000000000000000000000000000000">EXEC [model].[ModelItem_Generic_Delete]
#ObjectIdTable,
#MarkAsDeleted,
#DeletedBy,
#DeletedO</frame>
</executionStack>
<inputbuf>Proc [Database Id = 5 Object Id = 1697441121]</inputbuf>
</process>
<process id="processa9e0ddc108" taskpriority="0" logused="2657548" waitresource="KEY: 5:72057594060013568 (3f1e49aa6519)" waittime="2779" ownerId="19456397" transactionname="user_transaction" lasttranstarted="2018-05-21T14:43:30.350" XDES="0xa9dc49c458" lockMode="RangeS-S" schedulerid="2" kpid="55424" status="suspended" spid="85" sbid="2" ecid="0" priority="0" trancount="2" lastbatchstarted="2018-05-21T14:43:30.537" lastbatchcompleted="2018-05-21T14:43:30.530" lastattention="1900-01-01T00:00:00.530" clientapp=".Net SqlClient Data Provider" hostname="RD0003FF430FC8" hostpid="12344" loginname="officearchitect" isolationlevel="read committed (2)" xactid="19456397" currentdb="5" currentdbname="OfficeArchitect_Performance_Test" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
<executionStack>
<frame procname="d2558974-73ab-4869-acd2-9cce4009286e.model.ModelItem_Generic_Delete" queryhash="0xd6e2f8f770b21179" queryplanhash="0x18df7aa720a890f6" line="80" stmtstart="4110" stmtend="4360" sqlhandle="0x0300050096f1cb4302e72300e7a8000001000000000000000000000000000000000000000000000000000000">DELETE
MI
FROM
[model].ModelItem MI
INNER JOIN
#ModelItemIdTable MIT
ON
MIT.EntityId = MI.ModelItemI</frame>
<frame procname="d2558974-73ab-4869-acd2-9cce4009286e.model.ModelItem_Object_Delete" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="25" stmtstart="1088" stmtend="1302" sqlhandle="0x0300050061e52c65bce72300e7a8000001000000000000000000000000000000000000000000000000000000">EXEC [model].[ModelItem_Generic_Delete]
#ObjectIdTable,
#MarkAsDeleted,
#DeletedBy,
#DeletedO</frame>
</executionStack>
<inputbuf>Proc [Database Id = 5 Object Id = 1697441121]</inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594060013568" dbid="5" objectname="d2558974-73ab-4869-acd2-9cce4009286e.model.RelationshipPair" indexname="PK_RelationshipPair_RelationshipPairId" id="lockaa19259180" mode="RangeS-U" associatedObjectId="72057594060013568">
<owner-list>
<owner id="processa9f9857088" mode="RangeS-S" />
</owner-list>
<waiter-list>
<waiter id="processa9f6f73c28" mode="X" requestType="convert" />
</waiter-list>
</keylock>
<keylock hobtid="72057594060013568" dbid="5" objectname="d2558974-73ab-4869-acd2-9cce4009286e.model.RelationshipPair" indexname="PK_RelationshipPair_RelationshipPairId" id="lockaa18438980" mode="RangeX-X" associatedObjectId="72057594060013568">
<owner-list>
<owner id="processa9e0ddc108" mode="RangeS-S" requestType="wait" />
</owner-list>
<waiter-list>
<waiter id="processa9f9857088" mode="RangeS-S" requestType="wait" />
</waiter-list>
</keylock>
<keylock hobtid="72057594060013568" dbid="5" objectname="d2558974-73ab-4869-acd2-9cce4009286e.model.RelationshipPair" indexname="PK_RelationshipPair_RelationshipPairId" id="lockaa19259180" mode="RangeS-U" associatedObjectId="72057594060013568">
<owner-list>
<owner id="processa9f6f73c28" mode="U" />
<owner id="processa9f6f73c28" mode="X" requestType="convert" />
</owner-list>
<waiter-list>
<waiter id="processa9fb862108" mode="RangeS-S" requestType="wait" />
</waiter-list>
</keylock>
<keylock hobtid="72057594060013568" dbid="5" objectname="d2558974-73ab-4869-acd2-9cce4009286e.model.RelationshipPair" indexname="PK_RelationshipPair_RelationshipPairId" id="lockaa18438980" mode="RangeX-X" associatedObjectId="72057594060013568">
<owner-list>
<owner id="processa9fb862108" mode="RangeX-X" />
</owner-list>
<waiter-list>
<waiter id="processa9e0ddc108" mode="RangeS-S" requestType="wait" />
</waiter-list>
</keylock>
</resource-list>
</deadlock>
Now from what I can understand (this is a bit new to me), the DELETE RP statement was the one that was "victimized", this was due to the INSERT INTO [model].[ModelItem] statement.
The issue occurred with locking on the index PK_RelationshipPair_RelationshipPairId.
What I don't fully understand are the RangeA-B locks. I understand that a range of values are locked on the index. But not quite sure why.
I understand that without the actual sql code it is difficult to see exactly what is going on, but I need some assistance in regards to how to go about diagnosing this.
I've tried to replicate the deadlock by running the DELETE and INSERT INTO statements in two transactions (and not completing or rolling back the insert), but no deadlock so far.
Edit
Transaction scope in C# layer is set as follows
var transactionOptions = return new TransactionOptions
{
IsolationLevel = IsolationLevel.ReadCommitted,
Timeout = TransactionManager.MaximumTimeout
};
using (var transaction = new TransactionScope(TransactionScopeOption.Required, transactionOptions, TransactionScopeAsyncFlowOption.Enabled))
{
await action(transaction);
transaction.Complete();
}
It seems you are using TransactionScope class that is forcing range locks common on serializable isolation level despite you using read commited isolation level. Those range locks lockMode="RangeS-S" are prompt to blocking and deadlocks.`
Despite you specifying Read Committed on the transaction options, TransactionScope is forcing range locks that exist only on serializable isolation as explained here.

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 due to keylock involving 3 processes

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

Deadlocking issue in parallel batch processing (Invoice Posting ax 2009)

I am getting an error "Cannot select a record in SalesParmSubTable.
Deadlock, where one or more users have simultaneously locked the whole table or part of it".
I used sql profiler for getting the deadlock trace. Can someone please help to rectify it.
<?xml version="1.0" encoding="UTF-8"?>
<deadlock-list>
<deadlock victim="process58d048">
<process-list>
<process id="process58d048" taskpriority="0" logused="5784" waitresource="PAGE: 16:1:16714223" waittime="1587" ownerId="207752233" transactionname="user_transaction" lasttranstarted="2016-05-07T07:58:32.533" XDES="0x52376d950" lockMode="S" schedulerid="4" kpid="10860" status="suspended" spid="159" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2016-05-07T07:58:32.553" lastbatchcompleted="2016-05-07T07:58:32.553" clientapp="Microsoft Dynamics AX" hostname="*********" hostpid="19816" loginname="************" isolationlevel="read committed (2)" xactid="207752233" currentdb="16" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="100" sqlhandle="0x02000000d3d2cf1665ba9e17ee49d6dd9bc2fbf295ad7168">SELECT A.DEL_SALESID,A.SALESNAME,A.PARMID,A.ORIGSALESID,A.TABLEREFID,A.DEL_ORIGTABLEREFID,A.JOURNALREFTABLEID,A.JOURNALREFRECID,A.SUBID,A.CREATEDDATETIME,A.CREATEDBY,A.RECVERSION,A.RECID FROM SALESPARMSUBTABLE A WHERE ((A.DATAAREAID=#P1) AND (A.PARMID=#P2)) AND NOT EXISTS (SELECT 'x' FROM SALESPARMLINE B WHERE ((B.DATAAREAID=#P3) AND (((B.PARMID=A.PARMID) AND (B.TABLEREFID=A.TABLEREFID)) AND (B.ORIGSALESID=A.ORIGSALESID)))) ORDER BY A.DATAAREAID,A.PARMID,A.DEL_SALESID</frame>
<frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">unknown</frame>
</executionStack>
<inputbuf>(#P1 nvarchar(5),#P2 nvarchar(21),#P3 nvarchar(5))SELECT A.DEL_SALESID,A.SALESNAME,A.PARMID,A.ORIGSALESID,A.TABLEREFID,A.DEL_ORIGTABLEREFID,A.JOURNALREFTABLEID,A.JOURNALREFRECID,A.SUBID,A.CREATEDDATETIME,A.CREATEDBY,A.RECVERSION,A.RECID FROM SALESPARMSUBTABLE A WHERE ((A.DATAAREAID=#P1) AND (A.PARMID=#P2)) AND NOT EXISTS (SELECT 'x' FROM SALESPARMLINE B WHERE ((B.DATAAREAID=#P3) AND (((B.PARMID=A.PARMID) AND (B.TABLEREFID=A.TABLEREFID)) AND (B.ORIGSALESID=A.ORIGSALESID)))) ORDER BY A.DATAAREAID,A.PARMID,A.DEL_SALESID</inputbuf>
</process>
<process id="process572988" taskpriority="0" logused="5800" waitresource="PAGE: 16:1:16714223" waittime="1576" ownerId="207752211" transactionname="user_transaction" lasttranstarted="2016-05-07T07:58:32.530" XDES="0x865e1950" lockMode="S" schedulerid="3" kpid="21628" status="suspended" spid="154" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2016-05-07T07:58:32.563" lastbatchcompleted="2016-05-07T07:58:32.560" clientapp="Microsoft Dynamics AX" hostname="*********" hostpid="19816" loginname="*********" isolationlevel="read committed (2)" xactid="207752211" currentdb="16" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="100" sqlhandle="0x02000000d3d2cf1665ba9e17ee49d6dd9bc2fbf295ad7168">SELECT A.DEL_SALESID,A.SALESNAME,A.PARMID,A.ORIGSALESID,A.TABLEREFID,A.DEL_ORIGTABLEREFID,A.JOURNALREFTABLEID,A.JOURNALREFRECID,A.SUBID,A.CREATEDDATETIME,A.CREATEDBY,A.RECVERSION,A.RECID FROM SALESPARMSUBTABLE A WHERE ((A.DATAAREAID=#P1) AND (A.PARMID=#P2)) AND NOT EXISTS (SELECT 'x' FROM SALESPARMLINE B WHERE ((B.DATAAREAID=#P3) AND (((B.PARMID=A.PARMID) AND (B.TABLEREFID=A.TABLEREFID)) AND (B.ORIGSALESID=A.ORIGSALESID)))) ORDER BY A.DATAAREAID,A.PARMID,A.DEL_SALESID</frame>
<frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">unknown</frame>
</executionStack>
<inputbuf>(#P1 nvarchar(5),#P2 nvarchar(21),#P3 nvarchar(5))SELECT A.DEL_SALESID,A.SALESNAME,A.PARMID,A.ORIGSALESID,A.TABLEREFID,A.DEL_ORIGTABLEREFID,A.JOURNALREFTABLEID,A.JOURNALREFRECID,A.SUBID,A.CREATEDDATETIME,A.CREATEDBY,A.RECVERSION,A.RECID FROM SALESPARMSUBTABLE A WHERE ((A.DATAAREAID=#P1) AND (A.PARMID=#P2)) AND NOT EXISTS (SELECT 'x' FROM SALESPARMLINE B WHERE ((B.DATAAREAID=#P3) AND (((B.PARMID=A.PARMID) AND (B.TABLEREFID=A.TABLEREFID)) AND (B.ORIGSALESID=A.ORIGSALESID)))) ORDER BY A.DATAAREAID,A.PARMID,A.DEL_SALESID</inputbuf>
</process>
</process-list>
<resource-list>
<pagelock fileid="1" pageid="16714223" dbid="16" objectname="AX2009_****.dbo.SALESPARMLINE" id="lock562bdb480" mode="IX" associatedObjectId="72057635944464384">
<owner-list>
<owner id="process572988" mode="IX" />
</owner-list>
<waiter-list>
<waiter id="process58d048" mode="S" requestType="convert" />
</waiter-list>
</pagelock>
<pagelock fileid="1" pageid="16714223" dbid="16" objectname="AX2009_****.dbo.SALESPARMLINE" id="lock562bdb480" mode="IX" associatedObjectId="72057635944464384">
<owner-list>
<owner id="process58d048" mode="IX" />
</owner-list>
<waiter-list>
<waiter id="process572988" mode="S" requestType="convert" />
</waiter-list>
</pagelock>
</resource-list>
</deadlock>
</deadlock-list>
I don't think we can solve the deadlock issue by merely knowing a deadlock is occurring...we need to know why or what process is happening.
It looks like it's a cleanup operation or something with sales order posting, because it's looking for SalesParmTable records where the SalesParmLine records don't exist. You can use that query to look for the select statement in code and work backwards to see what process is initiating it.
I'd think a better approach would be, when it's happening, find out what users are doing at the time. Check batch jobs to see if two batch jobs are doing the same thing at the same time.
You may be able to find out what users/processes are causing it by following this blog:
https://blogs.msdn.microsoft.com/amitkulkarni/2011/08/10/finding-user-sessions-from-spid-in-dynamics-ax-2012/