I want to update a single record in a table to reflect that a given client session has acquired the record (and now owns it for further updates) within a multi-session environment. I’ve got this so far:
create procedure AcquireRow(
@itemNo int, -- Item ID to acquire
@sessNo int, -- Session ID
@res char(1) out) -- Result
as
begin
-- Attempt to acquire the row
update Items
set
State = 'A', -- 'A'=Acquired
SessionID = @sessNo
where ItemID = @itemNo
and State = 'N'; -- 'N'=Not acquired
-- Verify that the session actually acquired the row
set @res = 'T'; -- 'T'=Success
if @@rowcount = 0
set @res = 'F'; -- 'F'=Failure
end;
The out variable @state is set to 'T' if the procedure successfully acquired the row, otherwise it’s set to 'F' to indicate failure.
My question: Is this guaranteed to work atomically, so that only one session successfully acquires (updates) the row if several sessions call AcquireRow() at the same time? Or is there a better way of doing this? Do I need an explicit rowlock?
Amended:
Based on Remus’s answer, I would rearrange the code thus:
set @res = 'F';
update ...;
if @@rowcount > 0
set @res = 'T';
Using an output clause or assigning the resulting row’s ItemID to a variable within the update would also be prudent.
@@ROWCOUNTis notoriously easy to get wrong. For example, in your case. From MSDN:To be correct you should check the @@ROWCOUNT immediately after the
UPDATE. It is safer to use a ROWLOCK but is not necessary. Even if the optimizer decides to use page locks, the ‘acquire’ semantics are correct.I would probably prefer another approach, namely to use the
OUTPUTclause ofUPDATE:This scheme is more error proof and also more flexible, as it allows for acquiring of unknown ItemId: the id acquired is placed in the @updated table variable and can be returned to caller.
As a general note, using real, committed, updates for ‘acquire’ is riddled with problems as you cannot know what rows are really acquired and which ones are just abandoned (client disconnected or crashed w/o releasing the ‘acquisition’).