I have a table I'm using as a work queue. Essentially, it consists of a primary key, a piece of data, and a status flag (processed/unprocessed). I have multiple processes trying to grab the next unprocessed row, so I need to make sure that they observe proper lock and update semantics to avoid race condition nastiness. To that end, I've defined a stored procedure they can call:
CREATE PROCEDURE get_from_q
AS
DECLARE @queueid INT
BEGIN TRANSACTION TRAN1
SELECT TOP 1 @queueid = id FROM MSG_Q WITH (updlock, readpast) WHERE MSG_Q.status=0
SELECT TOP 1 * FROM MSG_Q WHERE MSG_Q.id=@queueid
UPDATE MSG_Q SET status=1 WHERE id=@queueid
COMMIT TRANSACTION TRAN1
Note the use of "WITH (updlock, readpast)" to make sure that I lock the target row and ignore rows that are similarly locked already.
Now, the procedure works as listed above, which is great. While I was putting this together, however, I found that if the second SELECT and the UPDATE are reversed in order (i.e. UPDATE first then SELECT), I got no data back at all. And no, it didn't matter whether the second SELECT was before or after the final COMMIT.
My question is thus why the order of the second SELECT and UPDATE makes a difference. I suspect that there is something subtle going on there that I don't understand, and I'm worried that it's going to bite me later on.
Any hints?