I have the following table structure (simplified, pseudocode):
TABLE dbo.Users (Id int NOT NULL IDENTITY PRIMARY KEY)
TABLE dbo.Transfers (
Id int NOT NULL IDENTITY PRIMARY KEY,
CreatedByUserId NOT NULL FOREIGN KEY REFERENCES dbo.Users (Id)
... -- no other user-related fields
)
I have the following situation in application code:
-- Given a User with Id 5 that already exists
BeginTransaction
1. INSERT INTO dbo.Users (...) VALUES (...) -- Creates user with Id 6
NotInTransaction.Start
2. INSERT dbo.Transfers (CreatedByUserId, ...) VALUES (5, ...)
NotInTransaction.End
...
CommitTransaction
In this specific case (when existing user and newly created user have nearby Ids), I get a timeout from step 2.
Now the interesting part is that I paused the application on step 2, and tried the following things outside the (paused) transaction:
SELECT * FROM dbo.Users WHERE UserId = @UserId
works (not locked) for all values of@UserIdexcept 6, which makes total senseINSERT dbo.Transfers (CreatedByUserId, ...) VALUES (@UserId, ...)
works for1,2,3,4, locked for6and5
I did a quick look on sys.dm_tran_locks (which I am not a master of) with some hobt joins, and when INSERT is locked, it shows
ObjectName | resource_type | resource_description | resource_associated_entity_id | request_mode | request_status
Users | KEY | (b9b173bbe8d5) | 72057594047299584 | X | GRANT
Users | KEY | (b9b173bbe8d5) | 72057594047299584 | S | WAIT
Now, the question is: why can I select user 5 from outside the transaction, but not insert into a table that references it through FK?