Słyszałem wcześniej o takich problemach dotyczących współbieżności w MySQL. Nie w Postgres.
Wystarczy wbudowane blokady na poziomie wiersza w domyślnym READ COMMITTED
poziomie izolacji transakcji .
Sugeruję pojedynczą instrukcję z modyfikującym dane CTE (coś, czego MySQL również nie ma), ponieważ wygodnie jest przekazywać wartości bezpośrednio z jednej tabeli do drugiej (jeśli jest to potrzebne). Jeśli nie potrzebujesz niczego z coupon
tabeli, możesz również użyć transakcji z osobnymi UPDATE
i INSERT
wyciągami.
WITH upd AS (
UPDATE coupon
SET used = true
WHERE coupon_id = 123
AND NOT used
RETURNING coupon_id, other_column
)
INSERT INTO log (coupon_id, other_column)
SELECT coupon_id, other_column FROM upd;
To powinno być rzadkie rzeczą, która stara się więcej niż jednej transakcji, aby odkupić ten sam kupon. Mają unikalny numer, prawda? Jednak więcej niż jedna transakcja w tym samym momencie powinna być znacznie rzadsza. (Może błąd aplikacji lub ktoś próbuje zagrać w system?)
Tak czy inaczej, UPDATE
tylko jedna transakcja się powiedzie, bez względu na wszystko. UPDATE
Nabywa blokady na poziomie wiersza w każdym wierszu docelowym przed aktualizacją. Jeśli współbieżna transakcja próbuje UPDATE
tego samego wiersza, zobaczy blokadę w wierszu i zaczeka na zakończenie transakcji blokującej ( ROLLBACK
lub COMMIT
), a następnie będzie pierwszą w kolejce blokowania:
W przypadku zatwierdzenia sprawdź ponownie warunek. Jeśli nadal jest NOT used
, zablokuj rząd i kontynuuj. W przeciwnym UPDATE
razie teraz nie znajdzie żadnego kwalifikującego się wiersza i nic nie robi, nie zwraca żadnego wiersza, więc INSERT
również nic nie robi.
Jeśli wycofano, zablokuj rząd i kontynuuj.
Nie ma możliwości wystąpienia warunków wyścigu .
Nie ma możliwości impasu, chyba że włożysz więcej zapisów w tę samą transakcję lub w inny sposób zablokujesz więcej wierszy niż tylko jeden.
INSERT
Jest beztroskie. Jeśli przez jakiś błąd coupon_id
już znajduje się w log
tabeli (i masz ograniczenie UNIKALNE lub PK log.coupon_id
), cała transakcja zostanie wycofana po unikalnym naruszeniu. Wskazuje na nielegalny stan w twojej bazie danych. Jeśli powyższa instrukcja jest jedynym sposobem zapisu do log
tabeli, nigdy nie powinno to nastąpić.