DETAIL: Key (proname, proargtypes, pronamespace)=(rand, , 2200) already exists.: CREATE OR REPLACE FUNCTION "rand"() RETURNS float AS
Como diabos isso é possível? Como pode CREATE OR REPLACE
falhar com já existe? Isso está em um contêiner docker, ambiente completamente intocado e é o único que o código é executado. Sim, pode ser executado repetidamente, mas é por isso que OR REPLACE
está lá. Eu não entendi. Aqui está o comando completo:
if (!$connection->query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'rand'")->fetchField()) {
$connection->query('CREATE OR REPLACE FUNCTION "rand"() RETURNS float AS
\'SELECT random();\'
LANGUAGE \'sql\'');
}
A
OR REPLACE
cláusula inCREATE FUNCTION
não se destina à execução paralela contínua, mas sim para evitar a eliminação da função quando queremos apenas atualizar o corpo. Do documento :Agora, se várias sessões emitirem o mesmo
CREATE OR REPLACE FUNCTION
em paralelo, há de fato uma condição de corrida potencial que é tratada com base no índice exclusivo em(proname, proargtypes, pronamespace)
, e retorna ao chamador como um erro.O erro mencionado na pergunta acontece se, por exemplo:
a sessão #1 inicia
CREATE OR REPLACE
e descobre que a função não existe, então ela a cria, inserindopg_proc
e bloqueando a(proname, proargtypes, pronamespace)
entrada correspondente no índice.a sessão #2 inicia
CREATE OR REPLACE
, descobre que a função não existe (porque #1 ainda não foi confirmada) e é colocada para aguardar o bloqueio do índice.a sessão nº 1 é confirmada.
a sessão #2 tenta inserir e falha.
Observe que não usar BEGIN/COMMIT explícito não altera a condição de corrida: os pares BEGIN/COMMIT são apenas implícitos em cada instrução SQL.
Para que essa sequência paralela funcione perfeitamente,
OR REPLACE
precisaria estar paraCREATE FUNCTION
o queON CONFLICT
está paraINSERT
, mas não é o caso.Para lidar com DDL conflitantes ou idênticos que podem ser executados em paralelo, provavelmente você deve usar um bloqueio explícito no início da sequência para colocá-lo como um todo em uma seção crítica. Bloqueios consultivos podem ser usados para isso. Ou você precisa implementar uma estratégia de "repetir em caso de falha" com pontos de salvamento em torno de declarações individuais.