Ho sentito parlare di problemi di concorrenza come quello in MySQL prima. Non così in Postgres.
I blocchi integrati a livello di riga nel livello di READ COMMITTED
isolamento della transazione predefinito sono sufficienti.
Suggerisco una singola istruzione con un CTE che modifica i dati (qualcosa che anche MySQL non ha) perché è conveniente passare direttamente i valori da una tabella all'altra (se necessario). Se non hai bisogno di nulla dalla coupon
tabella, puoi utilizzare anche una transazione con istruzioni separate UPDATE
e 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;
Dovrebbe essere raro che più di una transazione cerchi di riscattare lo stesso coupon. Hanno un numero univoco, no? Più di una transazione che prova nello stesso momento dovrebbe essere ancora più rara. (Forse un bug dell'applicazione o qualcuno sta cercando di giocare al sistema?)
Comunque sia, l' UPDATE
unica riuscita ha esattamente una sola transazione, qualunque cosa accada. Un UPDATE
acquisisce un blocco a livello di riga su ogni riga di destinazione prima dell'aggiornamento. Se una transazione simultanea tenta di eseguire UPDATE
la stessa riga, vedrà il blocco sulla riga e attenderà il completamento ( ROLLBACK
o COMMIT
) della transazione di blocco , quindi sarà la prima nella coda di blocco:
Se eseguito, ricontrollare la condizione. Se è ancora NOT used
, blocca la riga e procedi. Altrimenti UPDATE
ora non trova alcuna riga di qualificazione e non fa nulla , non restituisce alcuna riga, quindi INSERT
anche non fa nulla.
Se ripristinato, bloccare la riga e procedere.
Non esiste alcun potenziale per una condizione di gara .
Non esiste alcun potenziale per un deadlock a meno che non si inseriscano più scritture nella stessa transazione o si blocchi in altro modo più righe di una sola.
Il INSERT
è senza preoccupazioni. Se per qualche errore coupon_id
è già presente nella log
tabella (e hai un vincolo UNIQUE o PK attivato log.coupon_id
), l'intera transazione verrà ripristinata dopo una violazione unica. Indicherebbe uno stato illegale nel tuo DB. Se l'istruzione precedente è l'unico modo per scrivere nella log
tabella, ciò non dovrebbe mai accadere.