Le tue opzioni sono:
-
Esegui in
SERIALIZABLEisolamento. Le transazioni interdipendenti verranno interrotte al momento del commit a causa di un errore di serializzazione. Riceverai un sacco di spam nel registro degli errori e farai molti tentativi, ma funzionerà in modo affidabile. -
Definisci un
UNIQUEvincolo e riprova in caso di errore, come hai notato. Stessi problemi di cui sopra. -
Se è presente un oggetto padre, puoi
SELECT ... FOR UPDATEl'oggetto padre prima di eseguire il tuomaxinterrogazione. In questo caso dovrestiSELECT 1 FROM bar WHERE bar_id = $1 FOR UPDATE. Stai usandobarcome lucchetto per tutti ifoos con quelbar_id. Puoi quindi sapere che è sicuro procedere, a condizione che ogni query che esegue l'incremento del contatore lo faccia in modo affidabile. Questo può funzionare abbastanza bene.Questo esegue comunque una query aggregata per ogni chiamata, che (per opzione successiva) non è necessaria, ma almeno non invia spam al registro degli errori come le opzioni precedenti.
-
Usa un tavolo da banco. Questo è quello che farei. O in
baro in un tavolino comebar_foo_counter, acquisisci un ID riga utilizzandoUPDATE bar_foo_counter SET counter = counter + 1 WHERE bar_id = $1 RETURNING countero l'opzione meno efficiente se il tuo framework non è in grado di gestire
RETURNING:SELECT counter FROM bar_foo_counter WHERE bar_id = $1 FOR UPDATE; UPDATE bar_foo_counter SET counter = $1;Quindi, nella stessa transazione , usa la riga del contatore generata per il
number. Quando esegui il commit, la riga della tabella del contatore per quelbar_idviene sbloccato per la query successiva da utilizzare. Se esegui il rollback, la modifica viene annullata.
Raccomando l'approccio del contatore, utilizzando una tabella laterale dedicata per il contatore invece di aggiungere una colonna a bar . È più pulito da modellare e significa che crei meno aggiornamenti in bar , che può rallentare le query su bar .