Ever since CTP2 was released a few weeks back, I have become interested in the new In-Memory OLTP (aka Hekaton) feature currently being constructed for SQL Server 2014. Once I learned that Hekaton uses lock-free structures, one of the first things I started to wonder about is how Hekaton handles a classic deadlock scenario. After downloading and installing the CTP, I created a Hekaton-enabled database and then added a couple of tables:
create table CheckingAccount ( OwnerId int not null primary key nonclustered hash with (bucket_count = 1000), Balance money ) with (memory_optimized = on, durability = schema_and_data); create table SavingsAccount ( OwnerId int not null primary key nonclustered hash with (bucket_count = 1000), Balance money ) with (memory_optimized = on, durability = schema_and_data); insert CheckingAccount (OwnerId, Balance) values (123, 1000.00); insert SavingsAccount (OwnerId, Balance) values (123, 2000.00); |
Now in connection 1, I run the following:
begin transaction update SavingsAccount with (snapshot) set Balance -= 300.00 where OwnerId = 123; |
In connection 2:
begin transaction update CheckingAccount with (snapshot) set Balance -= 200.00 where OwnerId = 123; |
Back in connection 1:
update CheckingAccount with (snapshot) set Balance += 300.00 where OwnerId = 123; |
Msg 41302, Level 16, State 110, Line 1
The current transaction attempted to update a record that has been updated since this transaction started. The transaction was aborted.
Msg 3998, Level 16, State 1, Line 1
Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.
The statement has been terminated.
So right there we have the answer to how Hekaton handles this scenario: the second attempt at an update on the same row causes the transaction to fail.
Completing the scenario, switch back to connection 2 and run:
update SavingsAccount with (snapshot) set Balance += 200.00 where OwnerId = 123; commit transaction |
Now check the state of the tables:
select ca.OwnerId, ca.Balance CheckingBalance, sa.Balance SavingsBalance from CheckingAccount ca inner join SavingsAccount sa on ca.OwnerId = sa.OwnerId where ca.OwnerId = 123; |
So the tables are transactionally consistent. Bear in mind that in the classic version of this scenario (using disk-based tables), connection 2 would have been chose as the deadlock victim. It’s OK that in this world the opposite is the case because the rules of consistency don’t dictate who is the “winner” and who is the “loser,” only that end result “balances out.”