Do Isolamento de transação página:
Uma
EXPLAIN
nesse SELECT
pode dizer qual plano de consulta está sendo feito, mas se a tabela for pequena (ou vazia!), o PostgreSQL quase certamente escolherá uma varredura sequencial em vez de referenciar o índice. Isso causará um bloqueio de predicado em toda a tabela, causando falha de serialização sempre que outra transação fizer algo na tabela. No meu sistema:
isolation=# EXPLAIN SELECT * from mydevice where cid = 1;
QUERY PLAN
----------------------------------------------------------
Seq Scan on mydevice (cost=0.00..23.38 rows=5 width=46)
Filter: (cid = 1)
(2 rows)
Você pode tentar adicionar um índice e forçá-lo a usar isso:
isolation=# CREATE INDEX mydevice_cid_key ON mydevice (cid);
CREATE INDEX
isolation=# SET enable_seqscan = off;
SET
isolation=# EXPLAIN SELECT * from mydevice where cid = 1;
QUERY PLAN
----------------------------------------------------------------------------------
Index Scan using mydevice_cid_key on mydevice (cost=0.00..8.27 rows=1 width=46)
Index Cond: (cid = 1)
(2 rows)
No entanto, esta não é a solução correta. Vamos recuar um pouco.
Serializable destina-se a garantir que as transações tenham exatamente o mesmo efeito como se fossem executadas uma após a outra, apesar do fato de você estar executando essas transações simultaneamente. O PostgreSQL não possui recursos infinitos, portanto, embora seja verdade que ele coloca bloqueios de predicado nos dados que sua consulta realmente acessa, "dados" pode significar mais do que "linhas retornadas".
O PostgreSQL escolhe sinalizar falhas de serialização quando acha que pode haver um problema, não quando é certo. (Daí como ele generaliza bloqueios de linha para bloqueios de página.) Essa escolha de design causa falsos positivos, como o do seu exemplo. Os falsos positivos são menos do que ideais, no entanto, não afetam a correção da semântica de isolamento.
A mensagem de erro é:
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.
Essa dica é fundamental. Seu aplicativo precisa detectar falhas de serialização e repetir toda a operação . Isso é verdade sempre que
SERIALIZABLE
está em jogo -- ele garante a correção serial apesar da simultaneidade, mas não pode fazer isso sem a ajuda do seu aplicativo. Dito de outra forma, se você está realmente fazendo modificações simultâneas, a única maneira de o PostgreSQL satisfazer os requisitos de isolamento é pedir que seu aplicativo se serialize. Desta forma: