Condición de carrera de la cola de procesos de SQL Server
Tengo una cola de pedidos a la que acceden varios procesadores de pedidos a través de un procedimiento almacenado. Cada procesador pasa una identificación única que se utiliza para bloquear los siguientes 20 pedidos para su propio uso. Luego, el procedimiento almacenado devuelve estos registros al procesador de pedidos para que se actúe en consecuencia.
Hay casos en los que varios procesadores pueden recuperar el mismo registro 'OrderTable' y en ese momento intentan operar en él simultáneamente. En última instancia, esto da como resultado que se produzcan errores más adelante en el proceso.
Mi próximo curso de acción es permitir que cada procesador tome todos los pedidos disponibles y simplemente realice una rotación por turnos de los procesadores, pero esperaba simplemente hacer que esta sección del código sea segura y permitir que los procesadores tomen registros cuando lo deseen.
De manera explícita: alguna idea de por qué estoy experimentando esta condición de carrera y cómo puedo resolver el problema.
BEGIN TRAN
UPDATE OrderTable WITH ( ROWLOCK )
SET ProcessorID = @PROCID
WHERE OrderID IN ( SELECT TOP ( 20 )
OrderID
FROM OrderTable WITH ( ROWLOCK )
WHERE ProcessorID = 0)
COMMIT TRAN
SELECT OrderID, ProcessorID, etc...
FROM OrderTable
WHERE ProcessorID = @PROCID
Editar:
Busqué en Google para comprobar mi respuesta: "Procesamiento de colas de datos en SQL Server con READPAST y UPDLOCK" . Han pasado años desde que leí y jugué con esta solución.
Original:
Si utiliza la sugerencia READPAST, se omitirán las filas bloqueadas. Ha utilizado ROWLOCK, por lo que debe evitar la escalada de bloqueo. También necesitas UPDLOCK, como descubrí.
Entonces, el proceso 1 bloquea 20 filas, el proceso 2 tomará las siguientes 20, el proceso 3 toma las filas 41 a 60, etc.
La actualización también se puede escribir así:
UPDATE TOP (20)
foo
SET
ProcessorID = @PROCID
FROM
OrderTable foo WITH (ROWLOCK, READPAST, UPDLOCK)
WHERE
ProcessorID = 0
Actualizar, octubre de 2011
Esto se puede hacer de manera más elegante con la cláusula OUTPUT si necesita SELECT y UPDATE a la vez.
Puede utilizar Service Broker. También puedes usar sp_getapplock para serializar el acceso a tus filas, lo que eliminará las condiciones de carrera:
"Ayudando a la simultaneidad creando sus propios bloqueos (Mutexs en SQL)" http://sqlblogcasts.com/blogs/tonyrogerson/archive/2006/06/30/855.aspx