Mens Erwins forslag muligvis er det simpelste måde at få korrekt adfærd på (så længe du prøver din transaktion igen, hvis du får en undtagelse med SQLSTATE
af 40001), har kø-applikationer i sagens natur en tendens til at fungere bedre med anmodninger, der blokerer for en chance for at tage deres tur i køen end med PostgreSQL-implementeringen af SERIALIZABLE
transaktioner, hvilket tillader højere samtidighed og er noget mere "optimistisk" med hensyn til chancerne for kollision.
Eksempelforespørgslen i spørgsmålet, som det står, i standard READ COMMITTED
transaktionsisoleringsniveau ville tillade to (eller flere) samtidige forbindelser til begge at "kræve" den samme række fra køen. Hvad der vil ske er dette:
- T1 starter og når så langt som til at låse rækken i
UPDATE
fase. - T2 overlapper T1 i udførelsestid og forsøger at opdatere denne række. Den blokerer afventende
COMMIT
ellerROLLBACK
af T1. - T1 forpligter sig efter at have "gjort krav på" rækken.
- T2 forsøger at opdatere rækken, finder ud af, at T1 allerede har, leder efter den nye version af rækken, finder ud af, at den stadig opfylder udvælgelseskriterierne (som bare er det
id
matcher), og "gør krav på" rækken.
Det kan ændres til at fungere korrekt (hvis du bruger en version af PostgreSQL, som tillader FOR UPDATE
klausul i en underforespørgsel). Bare tilføj FOR UPDATE
til slutningen af underforespørgslen, som vælger id'et, og dette vil ske:
- T1 starter og låser nu rækken, før du vælger id.
- T2 overlapper T1 i udførelsestid og blokerer, mens du prøver at vælge et id, afventer
COMMIT
ellerROLLBACK
af T1. - T1 forpligter sig efter at have "gjort krav på" rækken.
- På det tidspunkt, T2 er i stand til at læse rækken for at se id'et, ser den, at den er blevet gjort krav på, så den finder det næste tilgængelige id.
Ved REPEATABLE READ
eller SERIALIZABLE
transaktionsisolationsniveau, ville skrivekonflikten give en fejl, som du kunne fange og fastslå var en serialiseringsfejl baseret på SQLSTATE, og prøve igen.
Hvis du generelt ønsker SERIALISERBARE transaktioner, men du vil undgå genforsøg i køområdet, kan du muligvis opnå det ved at bruge en rådgivende lås.