2012-04-21 14 views
5

Z dokumentacji dla find_or_create:Jak uniknąć warunków wyścigu podczas korzystania z metody find_or_create w DBIx :: Class :: ResultSet?

Uwaga: Ponieważ find_or_create() odczytuje z bazy danych, a następnie ewentualnie wkładki w oparciu o wynik, metoda ta jest przedmiotem wyścigu stanie. Inny proces mógł utworzyć rekord w tabeli po znalezieniu zakończonym i przed rozpoczęciem tworzenia. Aby uniknąć tego problemu, użyj find_or_create() wewnątrz transakcji.

Czy wystarczy użyć find_or_create() wewnątrz transakcji w PostgreSQL?

Odpowiedz

6

Nie, dokumentacja jest niepoprawna. Skorzystanie z samej transakcji nie pozwala na uniknięcie tego problemu. Gwarantuje to tylko, że cała transakcja zostanie wycofana, jeśli wystąpi wyjątek - aby w bazie danych nie było niespójnego stanu.

Do unikaj tego problemu, musisz zablokować tabelę - wewnątrz transakcji, ponieważ wszystkie blokady są zwalniane na końcu transakcji. Coś takiego:

BEGIN; 
LOCK TABLE mytbl IN SHARE MODE; 

-- do your find_or_create here 

COMMIT; 

Ale to nie jest magiczne lekarstwo na wszystko. Może to być problem z wydajnością i mogą występować zakleszczenia (równoczesne transakcje wzajemnie próbujące zablokować zasoby, które zostały już zablokowane). PostgreSQL wykryje taki warunek i anuluje wszystkie oprócz jednej z konkurencyjnych transakcji. Musisz być przygotowany na ponowienie operacji w przypadku niepowodzenia.

The PostgreSQL manual about locks.

Jeśli nie masz dużo współbieżności Państwo może też po prostu zignorować problem. Przedział czasowy jest bardzo mały, więc bardzo rzadko się zdarza. Jeśli zauważysz zduplikowany błąd naruszenia klucza, który nie zaszkodzi, to również to omówiłeś.

+2

Inne użyteczne strony. Dokumenty: http://www.postgresql.org/docs/current/interactive/mvcc.html Wersja PostgreSQL 9.1 lub późniejsza wersja szeregowalna: http://wiki.postgresql.org/wiki/SSI Inne poziomy izolacji lub wersje PostgreSQL: http : //www.postgresql.org/files/developer/concurrency.pdf – kgrittn

+0

Ale jaki jest właściwy sposób na złapanie "duplikatu błędu naruszenia klucza" w DBIC? –

+0

@eugeney: Przypuszczam, że otwierasz nowe pytanie zamiast komentarza. Zawsze możesz utworzyć link do tego, by zapisać sobie trochę pisania. –

0

Ta implementacja find_or_create powinny zapobiec sytuacji wyścigu, opisaną w PO:

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

Zmniejsza to także find_or_create() do jednego zapytania w najlepszym wypadku.

+1

Powoduje to odwrócenie logiki. Ale teraz masz mały przedział czasowy, w którym wpis może zostać usunięty - w takim przypadku logika się nie powiedzie. Próba napisania najpierw jest droższa niż próba czytania. Jest to tylko poprawa, jeśli duplikaty są bardzo rzadkie. Tak czy inaczej, konflikty powinny być bardzo rzadkie, ponieważ przedział czasowy jest niewielki. –

Powiązane problemy