2

We are building our own custom messaging system and are having concurrency issues. Here are the rules:

  1. A process (EXE) console application locks 3 records and returns them

  2. No other process running (we have 5 EXEs running) can pick any record that the other processes have already taken.

That simple, but yet, I'm puzzled.

Summary of the SQL Server stored procedure doing a "Lock And Peek":

The idea behind this that we reserve three "NEW" records and change their status to "IN PROGRESS" with a ROWLOCK on the SELECT and UPDATE statements. So in theory these records should be locked for one process so that other processes can't update or even select them. Can someone tell me what I'm doing wrong please?

ALTER PROCEDURE [dbo].[LockAndPeek] 
 @Count INT,
 @QueueTypeId INT
AS
BEGIN
 SET NOCOUNT ON;
 DECLARE @ListofIDs TABLE(ID INT);
 DECLARE @StatusIDInProgress INT
 SELECT @StatusIDInProgress = ID 
 FROM QueueStatuses (NOLOCK)
 WHERE Name = 'In Progress'
 INSERT INTO @ListofIDs (ID)
 SELECT TOP (@Count) Q.ID 
 FROM Queues Q (ROWLOCK) 
 INNER JOIN QueueStatuses QS (ROWLOCK) ON Q.StatusID = QS.ID
 WHERE QS.Name IN ('New', 'Errored') 
 AND Q.TypeID = @QueueTypeID 
 AND Q.AvailableTime IS NOT NULL 
 AND Q.AvailableTime <= GETUTCDATE()
 ORDER BY Q.ID
 UPDATE Q WITH (ROWLOCK)
 SET STATUSID = @StatusIDInProgress,
 PROCESSED = GETUTCDATE()
 FROM Queues Q (ROWLOCK) 
 INNER JOIN QueueStatuses QS (ROWLOCK) ON Q.StatusID = QS.ID 
 INNER JOIN @ListofIDs LI ON Q.ID = LI.ID
 WHERE QS.Name IN ('New', 'Errored')
 SELECT 
 Q.ID, Q.AvailableTime, Q.NumberOfTries,
 Q.Created, Q.Processed, Q.ErrorData,
 QT.ID QueueTypeID, QT.Name QueueTypeName,
 QS.ID QueueStatusID, QS.Name QueueStatusName,
 Q.Message
 FROM 
 Queues Q (NOLOCK) 
 INNER JOIN
 QueueStatuses QS (NOLOCK) ON Q.StatusID = QS.ID 
 INNER JOIN
 QueueTypes QT (NOLOCK) ON Q.TypeId = QT.ID 
 INNER JOIN
 @ListofIDs LI ON Q.ID = LI.ID
END
marc_s
9,0626 gold badges46 silver badges52 bronze badges
asked Jan 2, 2015 at 21:58
2
  • 1
    I don't see a transaction. Is there one? Commented Jan 2, 2015 at 22:50
  • 1
    Also, lock level hints are missing such as UPDLOCK. Also HOLDLOCK is missing. Commented Jan 2, 2015 at 22:50

1 Answer 1

2

The query looks so much simpler. There's an output keyword in SQL Server that works perfectly!

Here we go:

ALTER PROCEDURE [dbo].[LockAndPeek] 
 @Count INT,
 @QueueTypeId INT
AS
BEGIN
SET XACT_ABORT ON; -- blow up the whole tran on any errors
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN TRAN
UPDATE Q
SET 
 StatusID = 2, -- In Progress
 Processed = GETUTCDATE()
OUTPUT Inserted.*
FROM (
 SELECT TOP (@Count) * 
 FROM
 Queues WITH (READPAST, ROWLOCK)
 WHERE
 StatusID = 1 AND -- New 
 TypeID = @QueueTypeID AND
 AvailableTime IS NOT NULL AND
 AvailableTime <= GETUTCDATE()
 ORDER BY ID
) Q;
COMMIT TRAN;
END
answered Jan 2, 2015 at 23:02
1
  • 1
    There you go! The OUTPUT clause is great! Commented Jan 3, 2015 at 0:17

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.