He oído hablar de problemas de concurrencia como ese en MySQL antes. No es así en Postgres.
Los bloqueos de nivel de fila incorporados en el READ COMMITTED
nivel de aislamiento de transacción predeterminado son suficientes.
Sugiero una sola declaración con un CTE modificador de datos (algo que MySQL tampoco tiene) porque es conveniente pasar valores de una tabla a otra directamente (si es necesario). Si no necesita nada de la coupon
tabla, puede usar una transacción con declaraciones separadas UPDATE
y INSERT
también.
WITH upd AS (
UPDATE coupon
SET used = true
WHERE coupon_id = 123
AND NOT used
RETURNING coupon_id, other_column
)
INSERT INTO log (coupon_id, other_column)
SELECT coupon_id, other_column FROM upd;
Debería ser raro que más de una transacción intente canjear el mismo cupón. Tienen un número único, ¿no? Sin embargo, más de una transacción que intente en el mismo momento en el tiempo debería ser mucho más rara. (¿Tal vez un error de aplicación o alguien tratando de jugar el sistema?)
Sea como fuere, el UPDATE
único tiene éxito exactamente para una transacción, pase lo que pase. Un UPDATE
adquiere un bloqueo de nivel de fila en cada fila de destino antes de actualizar. Si una transacción concurrente intenta UPDATE
la misma fila, verá el bloqueo en la fila y esperará hasta que finalice la transacción de bloqueo ( ROLLBACK
o COMMIT
), luego será el primero en la cola de bloqueo:
Si se confirma, vuelva a verificar la condición. Si todavía está NOT used
, bloquee la fila y continúe. De lo contrario, UPDATE
ahora no encuentra ninguna fila de calificación y no hace nada , no devuelve ninguna fila, por lo que INSERT
tampoco hace nada.
Si retrocede, bloquee la fila y continúe.
No hay potencial para una condición de carrera .
No hay potencial para un punto muerto a menos que coloque más escrituras en la misma transacción o bloquee más filas que solo la única.
El INSERT
es sin preocupaciones. Si, por algún error, el coupon_id
ya está en la log
tabla (y tiene una restricción UNIQUE o PK activada log.coupon_id
), la transacción completa se revertirá después de una violación única. Indicaría un estado ilegal en su base de datos. Si la declaración anterior es la única forma de escribir en la log
tabla, eso nunca debería ocurrir.