views:

23

answers:

1

edit: SQL Server 2005

I have a customer application running on 5 separate servers. Each application is looking at a single scheduling table. I want to be sure that no machine can access the same record at the same time as any other machine. Each server is capable of processing 1 row only at a time. Basically, the application just selects the next record available that can be run. If nothing is selected, it simply does nothing and waits another minute and tries again.

[edit: To be more specific, no row is ever deleted from dbo.the_table. It simply marks IsProcessing=1 so that other machines will not pick that up]

My stored procedure (SQL Server) is as followed:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN

SET @ScheduleId = SELECT TOP 1 ScheduleId FROM dbo.the_table WHERE NextRun <= GETDATE() AND IsEnabled = 1

UPDATE dbo.the_table SET IsProcessing=1 WHERE ScheduleId = @ScheduleId

--Edit: Return this to the program so we can do stuff with the data
SELECT * FROM dbo.the_table WHERE ScheduleId = @ScheduleId

IF @@ERROR = 0
COMMIT TRAN
ELSE
ROLLBACK TRAN

I want to be certain that the while the transaction is in progress, any SELECT statement by the other machines will be blocked until the transaction is committed by the blocking transaction. (e.g., Machine A starts a transaction -- if B,C,D, or E try and SELECT, they will wait until the transaction is committed).

+2  A: 

What version of SQL Server are you on? If SQL2005+ you could do this all in one statement

;WITH s AS
(
SELECT TOP 1 * 
FROM dbo.the_table WHERE NextRun <= GETDATE() AND IsEnabled = 1
AND IsProcessing = 0 /*?*/
--ORDER BY ScheduleId  ?
)
UPDATE    s  WITH (ROWLOCK, READPAST)
SET     IsProcessing=1 
OUTPUT INSERTED.*  /*Return row that was updated*/
Martin Smith
+1 in addition I'd recommend going over http://rusanu.com/2010/03/26/using-tables-as-queues/ since `the_table` in this case is used as a queue.
Remus Rusanu
I'm terribly sorry. I missed an important part of the procedure. I've made the appropriate edits.
ZeroVector
@Remus, Thanks very useful link. @Zero - As far as I can see that could easily be accommodated by returning `INSERTED.*` and having the procedure that picks the row checking that `IsProcessing = 0` - or am I missing something?
Martin Smith
I just wanted to make sure that with the edit I made that your solution would still be viable since I am not a SQL Server guru. It sounds like your solution will work -- do I still need to include SET TRANSACTION ISOLATION LEVEL SERIALIZABLE and add my BEGIN TRAN / COMMIT TRAN ?
ZeroVector
@Zero - No you don't need any of that. It is a single statement in an implicit transaction anyway and see Remus's link for an explanation of the `LCK_M_U` locking and why you don't need to change the isolation level.
Martin Smith