I'm trying to solve an issue with SQL Server 2008 R2 and am grasping at straws.
Given that data is housed in leaf-nodes of the B-tree, and is also exists in indexes, that someone could read multiple values during a single UPDATE
statement?
Imagine one process updates a row:
--Change status from Pending -> Waiting
BEGIN TRANSACTION
UPDATE Transactions SET Status = 'Waiting'
WHERE TransactionID = 12345
COMMIT TRANSACTION
Is it possible that during this BEGIN TRANS; UPDATE; COMMIT
another process could read both new and old values?
I wonder this because the update does not alter a value in only one place. That value will exist in multiple places:
- leaf nodes of clustered index
- IX_Tranasctions_1
- IX_Tranasctions_2
- IX_Tranasctions_3
- IX_Tranasctions_4
- IX_Tranasctions_5
- ...
- IX_Tranasctions_n
If the update begins, it has to update values in all these locations. What happens if another process uses an index execution plan to find the value:
- IX_Clustered:
PendingWaiting - IX_Transactions_1:
PendingWaiting - IX_Transactions_2:
PendingWaiting - IX_Transactions_3: Pending
- IX_Transactions_4: Pending
- IX_Transactions_5: Pending
- ...
- IX_Transactions_n: Pending
At that instant, if a query is issued that looks at the value in the clustered index it will find Waiting.
But if a query uses IX_Transactions_4
it will find a value of Pending
.
Undocumenting the locking mechanism
The lock order internally is undocumented, but i assume SQL Server takes a shared lock on the clustered and non-clustered indexes:
- IX_Clustered: Shared
- IX_Transactions_1: Shared
- IX_Transactions_2: Shared
- IX_Transactions_3: Shared
- IX_Transactions_4: Shared
- IX_Transactions_5: Shared
Then upgrades those locks to Update locks:
- IX_Clustered:
SharedUpdate - IX_Transactions_1:
SharedUpdate - IX_Transactions_2:
SharedUpdate - IX_Transactions_3:
SharedUpdate - IX_Transactions_4:
SharedUpdate - IX_Transactions_5:
SharedUpdate
at this point, another process can still read the values in those 5 indexes; because selects are compatible with Update locks.
Then the update proceeds. If we take a picture at an instant in time:
- IX_Clustered:
SharedUpdateExclusive -> value changed - IX_Transactions_1:
SharedUpdateExclusive -> value changed - IX_Transactions_2:
SharedUpdateExclusive - IX_Transactions_3:
SharedUpdate - IX_Transactions_4:
SharedUpdate - IX_Transactions_5:
SharedUpdate
Another process can no longer read values from items with an Exclusive lock, but it can still read values from items that still have the Update lock (Shared locks are compatible with Update locks)
Unless of course that's not what happens
That only works if SQL only promotes to exclusive as needed. If, instead it goes:
- IX_Clustered:
SUIXX -> value changed - IX_Transactions_1:
SUIXX -> value changed - IX_Transactions_2:
SUIXX - IX_Transactions_3:
SUIX - IX_Transactions_4:
SUIX - IX_Transactions_5:
SUIX
And i can't type anymore; my will to continue is crushed. Like i said, i'm grasping at straws.