Medan Erwins förslag möjligen är det enklaste sätt att få korrekt beteende (så länge du försöker om din transaktion om du får ett undantag med SQLSTATE av 40001), tenderar köapplikationer till sin natur att fungera bättre med förfrågningar som blockerar för en chans att ta sin tur i kön än med PostgreSQL-implementeringen av SERIALIZABLE transaktioner, vilket tillåter högre samtidighet och är något mer "optimistisk" om riskerna för kollision.
Exempelfrågan i frågan, som den står, i standardvärdet READ COMMITTED transaktionsisoleringsnivån skulle tillåta två (eller flera) samtidiga anslutningar att båda "anspråka" samma rad från kön. Vad som kommer att hända är detta:
- T1 startar och når så långt som att låsa raden i
UPDATEfas. - T2 överlappar T1 i körningstid och försöker uppdatera den raden. Den blockerar i väntan på
COMMITellerROLLBACKav T1. - T1 begår, efter att ha "gjort anspråk på" raden.
- T2 försöker uppdatera raden, upptäcker att T1 redan har, letar efter den nya versionen av raden, upptäcker att den fortfarande uppfyller urvalskriterierna (vilket är just det
idmatchar), och "gör anspråk" på raden.
Den kan modifieras för att fungera korrekt (om du använder en version av PostgreSQL som tillåter FOR UPDATE klausul i en underfråga). Lägg bara till FOR UPDATE till slutet av underfrågan som väljer id, och detta kommer att hända:
- T1 startar och låser nu raden innan du väljer id.
- T2 överlappar T1 i exekveringstid och blockerar när man försöker välja ett id, i väntan på
COMMITellerROLLBACKav T1. - T1 begår, efter att ha "gjort anspråk på" raden.
- När T2 kan läsa raden för att se id:t ser den att det har gjorts anspråk på det, så det hittar nästa tillgängliga id.
Vid REPEATABLE READ eller SERIALIZABLE transaktionsisoleringsnivå, skulle skrivkonflikten skapa ett fel, som du kunde fånga och fastställa var ett serialiseringsfel baserat på SQLSTATE, och försök igen.
Om du i allmänhet vill ha SERIALISERBARA transaktioner men du vill undvika omförsök i köområdet, kanske du kan åstadkomma det genom att använda ett rådgivande lås.