Hver gang du anmoder om SERIALIZABLE
isolation vil DB forsøge at få samtidige sæt forespørgsler til at syne ud til at være udført serielt i forhold til de resultater, de producerer. Det er ikke altid muligt, f.eks. når to transaktioner har gensidige afhængigheder. I dette tilfælde vil PostgreSQL afbryde en af transaktionerne med en fejl ved serialiseringsfejl, der fortæller dig, at du skal prøve det igen.
Kode, der bruger SERIALIZABLE
skal altid være parat til at prøve transaktioner igen. Den skal kontrollere SQLSTATE
og gentag transaktionen for serialiseringsfejl.
Se dokumentationen til transaktionsisolering .
I dette tilfælde tror jeg, at din største misforståelse kan være at:
da det ikke er noget af den slags, er det en INSERT ... SELECT
der berører vo_business.repositoryoperation
til både læsning og skrivning. Det er ganske nok til at skabe en potentiel afhængighed med en anden transaktion, der gør det samme, eller en, der læser og skriver til tabellen på en anden måde.
Derudover kan den serialiserbare isolationskode under nogle omstændigheder degenerere til at holde blok-niveau afhængighedsinformation af effektivitetsmæssige årsager. Så det er måske ikke nødvendigvis en transaktion, der berører de samme rækker, bare den samme lagerblok, især under belastning.
PostgreSQL vil foretrække at afbryde en serialiserbar transaktion, hvis den ikke er sikker på, at den er sikker. Bevissystemet har begrænsninger. Så det er også muligt, at du lige har fundet en sag, der narrer den.
For at vide det sikkert skal jeg se begge transaktioner side om side, men her er et bevis, der viser en insert ... select
kan komme i konflikt med sig selv. Åbn tre psql
sessioner og kør:
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 vil begå bøde. session2 mislykkes med:
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.
Det er ikke den samme serialiseringsfejl som din sag og beviser ikke, at din sætninger kan være i konflikt med hinanden, men det viser, at en insert ... select
er ikke så atomart, som du troede.