I'm currently inserting a record into a SQL Server Table and then selecting the auto-increment ID as follows:
(@p0 int,@p1 nvarchar(8))INSERT INTO [dbo].[Tag]([Some_Int], [Tag])
VALUES (@p0, @p1)
SELECT CONVERT(Int,SCOPE_IDENTITY()) AS [value]
(This was generated using Linq-to-SQL). For some reason when I run this code inside a transaction using the TransactionScope object with a Serializable isolation level, SQL Server throws a deadlock error. I analyzed the deadlock graph events and found that the two processes involved were each waiting on the other to perform the convert operation, as I understand the following information:
<resource-list>
<keylock hobtid="72057594101170176" dbid="5" objectname="foo.dbo.Tag" indexname="PK_Tag_1" id="lockb77cdc0" mode="RangeS-S" associatedObjectId="72057594101170176">
<owner-list>
<owner id="processc9be40" mode="RangeS-S"/>
</owner-list>
<waiter-list>
<waiter id="processc9ae38" mode="RangeI-N" requestType="convert"/>
</waiter-list>
</keylock>
<keylock hobtid="72057594101170176" dbid="5" objectname="foo.dbo.Tag" indexname="PK_Tag_1" id="lockb77cdc0" mode="RangeS-S" associatedObjectId="72057594101170176">
<owner-list>
<owner id="processc9ae38" mode="RangeS-S"/>
</owner-list>
<waiter-list>
<waiter id="processc9be40" mode="RangeI-N" requestType="convert"/>
</waiter-list>
</keylock>
</resource-list>
My understanding was that the transaction scope would prevent the second process from performing the insert until the first had finished both inserting and selecting the identity. However this doesn't seem to be the case. Could anyone shed some light on the best approach to achieving what I require in a thread-safe way?
--Updated--
Just to note; I'm 99% sure that a connection isn't being shared between the two processes as each creates a new DataContext to communicate with the database.
--Updated Again--
Remus Rusanu pointed out that some omitted information was related to the problem, I tried to simplify the scenario based on the deadlock graph report, but I've extended the explanation here. Before I do the insert I perform an exists query on the table in question to determine if the tag already exists. If it does I end the transaction. If not the insert should go ahead and then I perform an update, not shown here, on a table that has Some_Int
as the primary key, though the update is purely for a last modified value. It may also be of importance that the Tag table has a clustered index composed of both the auto inc ID and Some_Int. I didn't think this last piece of information was of relevance as I have tried changing the table to only have the auto inc field as the primary key / clustered index to no avail.
Thanks.
Deadlocks occurs because, two concurrent transactions may overlap e lock different resources, both required by the other transaction to finish. So, SQL server must choose on transaction, kill it, and allow the other to continue. Thanks for contributing an answer to Stack Overflow!
Serialise mode basically pretends you're single user and throws away stuff that behaves otherwise (not much better than a deadlock) Repeatable Read is irrelevent So to extrapolate your answer: so long as you don't access inserted rows before the transaction commits there is no way for an INSERT to cause a deadlock.
The error message obviously was indicating a deadlock problem. As a first step, he decided to check the system_health session for the deadlocks. Transaction (Process ID XX) was deadlocked on lock resources with another process and has been chosen as the deadlock victim.
If there are a lot of deadlocks SQL Server automatically adjusts the frequency of the deadlock search, and back up to 5 seconds if deadlocks are no longer as frequent. How does SQL Server choose the victim? There are a couple of factors that come into play here. The first is the deadlock priority.
The 'convert' in question is a 'lock convert' from RangeS-S to RangeI-N, not related to the 'CONVERT' function in any way. The fact that you have RangeS-S locks already placed on the PK_Tag_1 index indicates that you're doing something more than just an INSERT. Does your transaction does, by any chance, a check first to see if the the new record 'exists' before attempting the insert?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With