TL;DR
SQL Server race condition on a round-robin process queue despite adding ROWLOCK
, READPAST
, and UPDLOCK
hints in conjunction with application locks.
I have an application where multiple app servers and multiple processes run "jobs" that are stored in a queue-like table in our SQL Server 2014 database. Every x number of seconds, each process queries the table for new jobs to run (if the process is available to take jobs). New jobs need to be assigned in something like a round-robin fashion on a user-to-user basis. The problem is that my current query is running into a race condition so that multiple processes are acting on the same job, resulting in duplicate data sets in our output tables. This is what the current query looks like:
EXEC sp_getapplock @Resource = 'GetJobAssignments', @LockMode = 'Exclusive', @LockOwner = 'Session';
BEGIN TRANSACTION;
WITH cte AS
(
SELECT TOP (@nMaxJobs) [JobId]
,[Owner]
,[Timeout]
,[StartTime]
,[Status]
,[userID]
, ROW_NUMBER() OVER (PARTITION BY [userID] ORDER BY [JobID]) AS [recID]
FROM [Jobs]
WITH (ROWLOCK, READPAST, UPDLOCK)
WHERE [Status] = 0 AND [Owner] is null
ORDER BY [recID], [userID]
)
UPDATE cte SET [Owner] = @owner, [Status] = 1;
COMMIT;
EXEC sp_releaseapplock @Resource = 'GetJobAssignments', @LockOwner = 'Session';
I have tried various combinations of ROWLOCK
, READPAST
, UPDLOCK
, and XLOCK
with no luck. Also used with and without the applock on both Session
and Transaction
settings. In other iterations of the query I messed around with putting the transaction isolation level to REPEATABLE READ
and SERIALIZABLE
with equal measures of no success.
Bearing in mind that I am neither an SQL nor a SQLServer expert, this next part might not mean anything...I have tried doing this within a stored procedure (as it is now), but in the past it has also been called from EF5 using dbContext.Database.ExecuteSqlCommand()
(it is now just a stored procedure that is part of the dbContext).
This current query is the result of messing around with a couple known working answers to questions similar to this:
SQL Server Process Queue Race Condition
https://dba.stackexchange.com/questions/81791/strategies-for-checking-out-records-for-processing
Outside of basic research on the things I have mentioned here, I am pretty unfamiliar with strategies in SQL Server to prevent deadlocks/race conditions. Are there any other options that I could try in my query, or are there any known preconditions that might exist that I could check for that would prevent this current strategy from working?