2012-04-21 8 views
5

Dalla documentazione per find_or_create:Come evitare le condizioni di gara quando si utilizza il metodo find_or_create di DBIx :: Class :: ResultSet?

Nota: A causa find_or_create() legge dal database e poi, eventualmente, inserti in base al risultato, questo metodo è soggetto a una gara condizione. Un altro processo potrebbe creare un record nella tabella dopo la ricerca è stata completata e prima dell'avvio della creazione. Per evitare questo problema, utilizzare find_or_create() all'interno di una transazione.

E 'sufficiente utilizzare solo find_or_create() all'interno di una transazione in PostgreSQL?

risposta

6

No, la documentazione non è corretta. L'utilizzo di una transazione da solo non fa non evitare questo problema. Garantisce solo il rollback dell'intera transazione se si verifica un'eccezione, in modo che nessuno stato incoerente venga mantenuto nel database.

Per evitano questo problema è necessario bloccare la tabella - all'interno di una transazione, in quanto tutti i blocchi vengono rilasciati al termine di una transazione. Qualcosa di simile:

BEGIN; 
LOCK TABLE mytbl IN SHARE MODE; 

-- do your find_or_create here 

COMMIT; 

Ma non è una cura magica per tutto. Può diventare un problema di prestazioni e potrebbero esserci problemi deadlock (transazioni simultanee che provano a bloccare le risorse che l'altro ha già bloccato). PostgreSQL rileverà tale condizione e annullerà tutte le transazioni concorrenti tranne una. Devi essere pronto a riprovare l'operazione in caso di fallimento.

The PostgreSQL manual about locks.

Se non si dispone di un sacco di concorrenza si potrebbe anche semplicemente ignorare il problema. La fascia oraria è molto piccola, quindi accade davvero molto raramente. Se riscontri l'errore di violazione della chiave duplicata, che non danneggerà, avrai coperto anche questo.

+2

Altre pagine utili. Documenti: http://www.postgresql.org/docs/current/interactive/mvcc.html PostgreSQL versione 9.1 o successiva implementazione serializzabile: http://wiki.postgresql.org/wiki/SSI Altri livelli di isolamento o versioni di PostgreSQL: http : //www.postgresql.org/files/developer/concurrency.pdf – kgrittn

+0

Ma qual è il modo corretto per rilevare "l'errore di violazione della chiave duplicata" in DBIC? –

+0

@eugeney: Suppongo che tu apra una nuova domanda per questo, invece di un commento. Puoi sempre collegarti a questo per risparmiarti un po 'di digitazione. –

0

Questa implementazione find_or_create dovrebbe impedire la condizione di competizione, descritto nel PO:

eval { 
    $row = $self->model->create({ ... }); 
} 
if([email protected] && [email protected] =~ /duplicate/i) { 
    $row = $self->model->find({ ... }); 
} 

Si riduce anche find_or_create() a una singola query nel migliore dei casi.

+1

Questo inverte la logica. Ma ora hai un piccolo intervallo temporale in cui la voce potrebbe essere cancellata - nel qual caso la logica fallirà. Cercare di scrivere per primo è più costoso che cercare di leggere. Quindi questo è solo un miglioramento se i duplicati sono molto rari. In entrambi i casi, i conflitti dovrebbero essere molto rari, perché la fascia oraria è piccola. –

Problemi correlati