Sempre que você solicitar
SERIALIZABLE
isolamento, o banco de dados tentará fazer conjuntos simultâneos de consultas parecer ter sido executado em série quanto aos resultados que produzem. Isso nem sempre é possível, por exemplo. quando duas transações têm dependências mútuas. Nesse caso, o PostgreSQL anulará uma das transações com um erro de falha de serialização, informando que você deve tentar novamente. Código que usa
SERIALIZABLE
deve estar sempre preparado para tentar novamente as transações. Ele deve verificar o SQLSTATE
e, para falhas de serialização, repita a transação. Consulte a documentação de isolamento de transações .
Nesse caso, acho que seu principal equívoco pode ser que:
como não é nada disso, é um
INSERT ... SELECT
que toca em vo_business.repositoryoperation
tanto para leitura quanto para escrita. Isso é o bastante para criar uma dependência potencial com outra transação que faça o mesmo, ou uma que leia e grave na tabela de outra maneira. Além disso, o código de isolamento serializável pode, em algumas circunstâncias, degenerar para manter informações de dependência em nível de bloco por motivos de eficiência. Portanto, pode não ser necessariamente uma transação tocando as mesmas linhas, apenas o mesmo bloco de armazenamento, especialmente sob carga.
O PostgreSQL preferirá abortar uma transação serializável se não tiver certeza de que é segura. O sistema de prova tem limitações. Portanto, também é possível que você tenha encontrado um caso que o engane.
Para ter certeza, eu precisaria ver as duas transações lado a lado, mas aqui está uma prova mostrando um
insert ... select
pode entrar em conflito consigo mesmo. Abra três psql
sessões e execute: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 fará o commit bem. session2 falhará com:
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.
Não é a mesma falha de serialização do seu caso e não prova que seu instruções podem entrar em conflito umas com as outras, mas mostra que um
insert ... select
não é tão atômico quanto você pensava.