När du begär SERIALIZABLE
isolering kommer DB att försöka få samtidiga uppsättningar av frågor att tyckas ha körts seriellt vad gäller de resultat de ger. Detta är inte alltid möjligt, t.ex. när två transaktioner har ömsesidiga beroenden. I det här fallet kommer PostgreSQL att avbryta en av transaktionerna med ett serialiseringsfel som talar om för dig att du bör försöka igen.
Kod som använder SERIALIZABLE
måste alltid vara beredd att ompröva transaktioner. Den måste kontrollera SQLSTATE
och, för serialiseringsfel, upprepa transaktionen.
Se dokumentationen för transaktionsisolering .
I det här fallet tror jag att din största missuppfattning kan vara att:
eftersom det inte är något sådant är det en INSERT ... SELECT
som rör vo_business.repositoryoperation
för både läsning och skrivning. Det är tillräckligt för att skapa ett potentiellt beroende med en annan transaktion som gör detsamma, eller en som läser och skriver till tabellen på annat sätt.
Dessutom kan den serialiserbara isoleringskoden under vissa omständigheter degenerera till att innehålla beroendeinformation på blocknivå av effektivitetsskäl. Så det kanske inte nödvändigtvis är en transaktion som rör samma rader, bara samma lagringsblock, särskilt under belastning.
PostgreSQL föredrar att avbryta en serialiserbar transaktion om den inte är säker på att den är säker. Bevissystemet har begränsningar. Så det är också möjligt att du precis har hittat ett fall som lurar det.
För att veta säkert skulle jag behöva se båda transaktionerna sida vid sida, men här är ett bevis som visar en insert ... select
kan komma i konflikt med sig själv. Öppna tre psql
sessioner och 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 kommer att begå böter. session2 kommer att misslyckas 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 är inte samma serialiseringsfel som ditt fall och bevisar inte att din satser kan komma i konflikt med varandra, men det visar att en insert ... select
är inte så atomär som du trodde.