PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

CREATE SCHEMA IF NOT EXISTS gera erro de chave duplicada


Isso é um pouco problemático na implementação de IF NOT EXISTS para tabelas e esquemas. Basicamente, eles são uma tentativa de upsert, e o PostgreSQL não lida com as condições de corrida de forma limpa. É seguro, mas feio.

Se o esquema está sendo criado simultaneamente em outra sessão, mas ainda não foi confirmado, ele existe e não existe, dependendo de quem você é e de sua aparência. Não é possível que outras transações "vejam" o novo esquema nos catálogos do sistema porque ele não está confirmado, então é entrada em pg_namespace não é visível para outras transações. Então CREATE SCHEMA / CREATE TABLE tenta criá-lo porque, no que lhe diz respeito, o objeto não existe.

No entanto, isso insere uma linha em uma tabela com uma restrição exclusiva. As restrições exclusivas devem poder ver linhas não confirmadas para funcionar. Então o insert bloqueia (para) até a primeira transação que fez o CREATE ou confirma ou reverte. Se confirmar, a segunda transação será abortada, porque tentou inserir uma linha que viola uma restrição exclusiva. CREATE SCHEMA não é inteligente o suficiente para pegar este caso e tentar novamente.

Para corrigir isso corretamente, o PostgreSQL provavelmente precisaria de bloqueio de predicado, onde poderia bloquear o potencial para uma linha . Isso pode ser adicionado como parte do trabalho atual em andamento para implementar o UPSERT .

Para esses comandos em particular, o PostgreSQL provavelmente poderia fazer uma leitura suja dos catálogos do sistema, onde pode ver as alterações não confirmadas. Em seguida, ele pode esperar que a transação não confirmada seja confirmada ou revertida, refaça a leitura suja para ver se alguém está esperando e tente novamente. Mas isso teria uma condição de corrida em que outra pessoa poderia criar o esquema entre quando você faz a leitura para verificar e quando você tenta criá-lo.

Portanto, o IF NOT EXISTS variantes teriam que:
  • Verifique se o esquema existe; se isso acontecer, termine sem fazer nada.
  • Tente criar a tabela
  • Se a criação falhar devido a um erro de restrição exclusivo, tente novamente no início
  • Se a criação da tabela for bem-sucedida, termine

Até onde eu sei, ninguém implementou isso, ou eles tentaram e não foi aceito. Haveria possíveis problemas com a taxa de queima de ID de transação, etc., com essa abordagem.

Eu acho que isso é um tipo de bug, mas é um tipo de bug do tipo "sim, nós sabemos", não um tipo de bug "vamos corrigir isso". Sinta-se à vontade para postar no pgsql-bugs sobre isso; no mínimo a documentação deve mencionar esta advertência sobre IF NOT EXISTS .

Eu não recomendo fazer DDL simultaneamente assim.