Zatímco Erwinův návrh je možná nejjednodušší způsob, jak získat správné chování (pokud zopakujete transakci, pokud získáte výjimku pomocí SQLSTATE
z 40001), aplikace ve frontě svou povahou mají tendenci pracovat lépe s blokováním požadavků, aby se dostaly na řadu ve frontě, než s implementací PostgreSQL SERIALIZABLE
transakce, což umožňuje vyšší souběžnost a je poněkud "optimističtější" ohledně možnosti kolize.
Příklad dotazu v otázce tak, jak je, ve výchozím nastavení READ COMMITTED
úroveň izolace transakce by umožnila dvě (nebo více) souběžná připojení k oběma "nárokovat" stejný řádek z fronty. Stane se toto:
- T1 se spustí a dostane se až k uzamčení řádku v
UPDATE
fáze. - T2 se v době provádění překrývá s T1 a pokouší se aktualizovat tento řádek. Blokuje čekající na
COMMIT
neboROLLBACK
z T1. - T1 provede potvrzení po úspěšném "nárokování" řádku.
- T2 se pokusí aktualizovat řádek, zjistí, že T1 již má, hledá novou verzi řádku, zjistí, že stále splňuje kritéria výběru (což je právě to
id
zápasy) a také „nárokuje“ řádek.
Může být upraven tak, aby fungoval správně (pokud používáte verzi PostgreSQL, která umožňuje FOR UPDATE
klauzule v dílčím dotazu). Stačí přidat FOR UPDATE
na konec dílčího dotazu, který vybere id, a stane se to:
- T1 spustí a nyní uzamkne řádek před výběrem id.
- T2 se překrývá s T1 v době provádění a blokuje při pokusu o výběr ID, dokud nebude
COMMIT
neboROLLBACK
z T1. - T1 provede potvrzení po úspěšném "nárokování" řádku.
- V době, kdy bude T2 schopen číst řádek, aby viděl ID, vidí, že byl nárokován, takže najde další dostupné ID.
Na stránce REPEATABLE READ
nebo SERIALIZABLE
úroveň izolace transakce, konflikt zápisu by vyvolal chybu, kterou byste mohli zachytit a určit, že šlo o selhání serializace na základě SQLSTATE, a zkuste to znovu.
Pokud obecně chcete SERIALIZAČNÍ transakce, ale chcete se vyhnout opakovaným pokusům v oblasti fronty, můžete toho dosáhnout pomocí poradního zámku.