I have read a lot about preventing race conditions, but typically with one record in an upsert scenario. For example: Atomic UPSERT in SQL Server 2005
I have a different requirement, and it is to prevent race conditions across multiple rows. For example, say I have the following table structure:
GiftCards:
GiftCardId int primary key not null,
OriginalAmount money not null
GiftCardTransactions:
TransactionId int primary key not null,
GiftCardId int (foreign key to GiftCards.GiftCardId),
Amount money not null
There could be multiple processes inserting into GiftCardTransactions
and I need to prevent inserting if SUM(GiftCardTransactions.Amount) + insertingAmount
would go over GiftCards.OriginalAmount
.
I know I could use TABLOCKX
on GiftCardTransactions
, but obviously this would not be feasible for lots of transactions. Another way would be to add a GiftCards.RemainingAmount
column and then I only need to lock one row (though with possibility of lock escalation), but unfortunately this isn't an option for me at this time (would this have been the best option?).
Instead of trying to prevent inserting in the first place, maybe the answer is to just insert, then select SUM(GiftCardTransactions.Amount)
, and rollback if necessary. This is an edge case, so I'm not worried about unnecessarily using up PK values, etc.
So the question is, without modifying the table structure and using any combination of transactions, isolation levels and hints, how can I achieve this with a minimal amount of locking?