PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

PostgreSQL e ActiveRecord sottoselezione per race condition

Le tue opzioni sono:

  • Esegui in SERIALIZABLE isolamento. 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 UNIQUE vincolo e riprova in caso di errore, come hai notato. Stessi problemi di cui sopra.

  • Se è presente un oggetto padre, puoi SELECT ... FOR UPDATE l'oggetto padre prima di eseguire il tuo max interrogazione. In questo caso dovresti SELECT 1 FROM bar WHERE bar_id = $1 FOR UPDATE . Stai usando bar come lucchetto per tutti i foo s con quel bar_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 bar o in un tavolino come bar_foo_counter , acquisisci un ID riga utilizzando

    UPDATE bar_foo_counter SET counter = counter + 1
    WHERE bar_id = $1 RETURNING counter
    

    o 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 quel bar_id viene 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 .