Всеки път, когато заявите SERIALIZABLE
изолация, DB ще се опита да направи едновременни набори от заявки да изглежда, че са изпълнени серийно по отношение на резултатите, които произвеждат. Това не винаги е възможно, напр. когато две транзакции имат взаимна зависимост. В този случай PostgreSQL ще прекъсне една от транзакциите с грешка при неуспешно сериализиране, което ви казва, че трябва да опитате отново.
Код, който използва SERIALIZABLE
трябва винаги да са готови да опитат отново транзакции. Трябва да провери SQLSTATE
и при неуспешна сериализация повторете транзакцията.
Вижте документацията за изолиране на транзакция .
В този случай мисля, че основното ви погрешно разбиране може да е следното:
тъй като не е нищо подобно, това е INSERT ... SELECT
който докосва vo_business.repositoryoperation
както за четене, така и за писане. Това е напълно достатъчно, за да създадете потенциална зависимост с друга транзакция, която прави същото, или такава, която чете и записва в таблицата по друг начин.
Освен това сериализуемият изолационен код може при определени обстоятелства да се дегенерира в съхраняване на информация за зависимост на ниво блок от съображения за ефективност. Така че може да не е непременно транзакция, засягаща същите редове, само същия блок за съхранение, особено при натоварване.
PostgreSQL ще предпочете да прекрати транзакция, която може да бъде сериализирана, ако не е сигурен, че е безопасна. Системата за доказване има ограничения. Така че също е възможно току-що да сте открили случай, който го заблуждава.
За да знам със сигурност, ще трябва да видя двете транзакции една до друга, но ето доказателство, показващо insert ... select
може да влезе в конфликт със себе си. Отворете три psql
сесии и стартирайте:
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 ще се ангажира добре. session2 ще се провали с:
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.
Това не е същата неуспешна сериализация като вашия случай и не доказва, че вашият изразите могат да са в конфликт помежду си, но показва, че insert ... select
не е толкова атомно, колкото си мислите.