Za każdym razem, gdy poprosisz o SERIALIZABLE
izolacja, że DB będzie próbował wykonać współbieżne zestawy zapytań wydaje się, że zostały wykonane seryjnie pod względem uzyskiwanych wyników. Nie zawsze jest to możliwe, m.in. gdy dwie transakcje mają wzajemne zależności. W takim przypadku PostgreSQL przerwie jedną z transakcji z błędem niepowodzenia serializacji, mówiącym, że powinieneś spróbować ponownie.
Kod, który używa SERIALIZABLE
musi być zawsze przygotowany na ponowną próbę transakcji. Musi sprawdzić SQLSTATE
a w przypadku niepowodzeń serializacji powtórz transakcję.
Zobacz dokumentację dotyczącą izolacji transakcji .
W tym przypadku myślę, że Twoim głównym nieporozumieniem może być to, że:
ponieważ nie jest to nic w tym rodzaju, jest to INSERT ... SELECT
dotyka vo_business.repositoryoperation
do czytania i pisania. To wystarczy, aby stworzyć potencjalną zależność z inną transakcją, która robi to samo, lub taką, która odczytuje i zapisuje do tabeli w inny sposób.
Dodatkowo, serializowalny kod izolacji może w pewnych okolicznościach zdegenerować się do przechowywania informacji o zależnościach na poziomie bloku ze względu na wydajność. Więc niekoniecznie musi to być transakcja dotykająca tych samych wierszy, tylko tego samego bloku pamięci, szczególnie pod obciążeniem.
PostgreSQL woli przerwać transakcję, którą można serializować, jeśli nie jest pewien, czy jest bezpieczna. System dowodowy ma ograniczenia. Możliwe więc, że właśnie znalazłeś przypadek, który go oszuka.
Aby mieć pewność, muszę zobaczyć obie transakcje obok siebie, ale oto dowód pokazujący insert ... select
może kłócić się z samym sobą. Otwórz trzy psql
sesje i przebieg:
session0: CREATE TABLE serialdemo(x integer, y integer);
session0: BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
session0: LOCK TABLE serialdemo IN ACCESS EXCLUSIVE MODE;
session1: BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
session2: BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
session1: INSERT INTO serialdemo (x, y)
SELECT 1, 2
WHERE NOT EXISTS (SELECT 1 FROM serialdemo WHERE x = 1);
session2: INSERT INTO serialdemo (x, y)
SELECT 1, 2
WHERE NOT EXISTS (SELECT 1 FROM serialdemo WHERE x = 1);
session0: ROLLBACK;
session1: COMMIT;
session2: COMMIT;
session1 zobowiąże się dobrze. sesja2 zakończy się niepowodzeniem z:
ERROR: could not serialize access due to read/write dependencies among transactions
DETAIL: Reason code: Canceled on identification as a pivot, during commit attempt.
HINT: The transaction might succeed if retried.
To nie jest ten sam błąd serializacji, co w Twoim przypadku i nie dowodzi, że Twój instrukcje mogą być ze sobą w konflikcie, ale pokazuje, że insert ... select
nie jest tak atomowy, jak myślałeś.