J'ai déjà entendu parler de problèmes de concurrence comme celui-ci dans MySQL. Ce n'est pas le cas à Postgres.
Les verrous intégrés au niveau des lignes dans le READ COMMITTED
niveau d'isolement des transactions par défaut sont suffisants.
Je suggère une seule déclaration avec un CTE de modification des données (quelque chose que MySQL n'a pas non plus) car il est pratique de passer directement les valeurs d'une table à l'autre (si vous en avez besoin). Si vous n'avez besoin de rien de la coupon
table, vous pouvez également utiliser une transaction avec des instructions distinctes UPDATE
et INSERT
.
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;
Il devrait être rare que plus d'une transaction essaie de racheter le même coupon. Ils ont un numéro unique, non? Plus d'une transaction essayant au même moment devrait être encore beaucoup plus rare. (Peut-être un bug d'application ou quelqu'un essayant de jouer au système?)
Quoi qu'il en soit, le UPDATE
seul réussit pour exactement une transaction, quoi qu'il arrive . An UPDATE
acquiert un verrou de niveau ligne sur chaque ligne cible avant la mise à jour. Si une transaction simultanée essaie UPDATE
la même ligne, elle verra le verrou sur la ligne et attendra que la transaction de blocage soit terminée ( ROLLBACK
ou COMMIT
), puis être la première dans la file d'attente de verrouillage:
Si commis, revérifiez la condition. Si c'est encore NOT used
, verrouillez la rangée et continuez. Sinon, le UPDATE
ne trouve maintenant aucune ligne de qualification et ne fait rien , ne renvoyant aucune ligne, donc le INSERT
ne fait rien non plus.
S'il est reculé, verrouillez la rangée et continuez.
Il n'y a aucun potentiel de condition de course .
Il n'y a aucun risque de blocage à moins que vous ne mettiez plus d'écritures dans la même transaction ou que vous ne bloquiez autrement plus de lignes que la seule.
Le INSERT
est sans souci. Si, par erreur, le coupon_id
déjà est dans la log
table (et que vous avez une contrainte UNIQUE ou PK activée log.coupon_id
), la transaction entière sera annulée après une violation unique. Indiquerait un état illégal dans votre base de données. Si l'instruction ci-dessus est le seul moyen d'écrire dans la log
table, cela ne devrait jamais se produire.