Kdykoli požadujete SERIALIZABLE
izolace se DB pokusí, aby se souběžné sady dotazů zdály jako sériově provedené pokud jde o výsledky, které produkují. Ne vždy je to možné, např. když dvě transakce mají vzájemné závislosti. V tomto případě PostgreSQL zruší jednu z transakcí s chybou selhání serializace, která vám říká, že byste to měli zkusit znovu.
Kód, který používá SERIALIZABLE
musí být vždy připraveni transakci opakovat. Musí zkontrolovat SQLSTATE
a v případě selhání serializace transakci opakujte.
Viz dokumentace k izolaci transakcí .
V tomto případě si myslím, že vaším hlavním nedorozuměním může být toto:
protože to není nic takového, je to INSERT ... SELECT
který se dotkne vo_business.repositoryoperation
jak pro čtení, tak pro psaní. To je docela dost k vytvoření potenciální závislosti na jiné transakci, která dělá totéž, nebo na transakci, která čte a zapisuje do tabulky jiným způsobem.
Kromě toho se může serializovatelný izolační kód za určitých okolností z důvodu efektivity zvrhnout na uchování informací o závislosti na úrovni bloku. Nemusí se tedy nutně jednat o transakci dotýkající se stejných řádků, pouze stejného úložného bloku, zejména při zatížení.
PostgreSQL upřednostní přerušení serializovatelné transakce, pokud si není jistý, zda je bezpečná. Systém důkazů má omezení. Takže je také možné, že jste právě našli případ, který to oklamal.
Abych to věděl jistě, potřeboval bych vidět obě transakce vedle sebe, ale zde je důkaz ukazující insert ... select
může být v rozporu se sebou samým. Otevřete tři psql
relací a spuštění:
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 bude v pořádku. session2 selže s:
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.
Nejde o stejné selhání serializace jako ve vašem případě a nedokazuje to, že váš příkazy mohou být ve vzájemném konfliktu, ale ukazuje to, že insert ... select
není tak atomový, jak jste si mysleli.