Restauração falha - não foi possível criar índice único

Olá, a restauração do backup falhou com o erro:

CREATE INDEX
ERRO: não foi possível criar o índice único "index_incoming_referers_on_path_and_incoming_domain_id"
DETALHE: A chave (path, incoming_domain_id)=(/m/search, 25) está duplicada.
EXCEÇÃO: psql falhou: DETALHE: A chave (path, incoming_domain_id)=(/m/search, 25) está duplicada.

Encontrei um tópico semelhante, mas não consegui entender o que precisa ser feito: https://meta.discourse.org/t/getting-this-error-during-restore-could-not-create-unique-index/

Agradeço qualquer ajuda passo a passo para leigos para resolver o problema.

Esse problema com a tabela incoming_referers tem surgido algumas vezes recentemente. Não tenho certeza do motivo pelo qual essa tabela específica está causando problemas, mas parece provável que os problemas estejam relacionados. Talvez alguém mais da equipe do Discourse tenha ideias sobre o que pode estar causando a criação de registros duplicados.

Você ainda tem acesso ao site no qual criou o arquivo de backup? Se sim, a correção é excluir o registro duplicado do banco de dados e, em seguida, criar um novo arquivo de backup. Para fazer isso, você deve acessar via SSH o servidor antigo e cd para o diretório /var/discourse:

cd /var/discourse

Em seguida, execute

./launcher enter app

Depois, entre no console do Rails com

rails c

Você deverá ver um prompt semelhante a este:

[1] pry(main)>

Tente executar o seguinte comando no console do Rails e nos informe o que ele retorna:

IncomingReferer.where(path: "/m/search")

Deve retornar um array com dois ou mais registros.

Obrigado.
Vou executar isso pela manhã e darei um retorno.

Isso é da instalação antiga — parece ser apenas um registro?

[1] pry(main)> IncomingReferer.where(path: "/m/search")
=> [#<IncomingReferer:0x00005638d834b130
  id: 5153,
  path: "/m/search",
  incoming_domain_id: 25>]
[2] pry(main)>

Edição: tentei também no novo servidor. Ele mostra:

[1] pry(main)> IncomingReferer.where(path: "/m/search")
=> []
[2] pry(main)>

Obrigado por verificar isso! O resultado que você obteve é exatamente o mesmo que eu vi em outro site mais cedo hoje. É um problema solucionável, mas vou tentar conseguir que um de nossos engenheiros dê uma olhada no que está acontecendo.

Meu principal motivo para migrar os servidores era o fato de eu estar no Debian 8, que está prestes a perder o suporte.
Com esse problema de restauração, optei por fazer a atualização para o Debian 9 no mesmo servidor. O processo foi bem-sucedido, então, por enquanto, há um alívio.
Obrigado pelo seu apoio.

Substitua esta linha

Você precisa fazer uma busca difusa para que ele não assuma que o índice funcione. Um único caractere de porcentagem provavelmente é suficiente se estiver no início, eu acho.

Você pode simplesmente excluir o registro extra. Para fazer isso corretamente, no entanto, você precisa atualizar a outra tabela que faz referência a esta. Tenho que procurar isso toda vez, pois há algumas tabelas diferentes que se relacionam com ela.

Esse problema é atribuído a extensões de terceiros, o que não faz muito sentido. Parece que deve ser culpa do PostgreSQL, mas não tenho certeza. Encontro isso algumas vezes por mês, parece (score) em vários sites).

Também estou com um problema de chave duplicada. Existe uma correção documentada?

discourse=# REINDEX SCHEMA CONCURRENTLY public;
    ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_ccnew"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 1905) is duplicated.

[1] pry(main)> IncomingReferer.where(path: "/m/search")
=> [#<IncomingReferer:0x0000557176d3f210 id: 44231, path: "/m/search", incoming_domain_id: 4>,
 #<IncomingReferer:0x0000557176d925c8 id: 42228, path: "/m/search", incoming_domain_id: 26>]

Embora eu tenha atualizado meu servidor no local e, portanto, não restaurarei mais para um novo servidor, tentei isso por curiosidade e não encontrei nenhum registro com pesquisa difusa:

[1] pry(main)> IncomingReferer.where(path: "%/m/search%")
=> []
[2] pry(main)> IncomingReferer.where(path: "%/m/search")
=> []
[3] pry(main)> IncomingReferer.where(path: "/m/search%")
=> []

Você precisa usar LIKE para que os curingas funcionem:

IncomingReferer.where("path LIKE '%/m/search%'")

Isso trouxe mais algumas chaves duplicadas.

[1] pry(main)> IncomingReferer.where("path LIKE '%/m/search%'")
=> [#<IncomingReferer:0x0000557eaa7ed488 id: 408, path: "/m/search", incoming_domain_id: 26>,
 #<IncomingReferer:0x0000557eaabd80c0 id: 1508, path: "/m/search", incoming_domain_id: 45>,
 #<IncomingReferer:0x0000557eaabe3268 id: 2216, path: "/m/search", incoming_domain_id: 420>,
 #<IncomingReferer:0x0000557eaabe2f20 id: 3081, path: "/m/search", incoming_domain_id: 230>,
 #<IncomingReferer:0x0000557eaabe2c00 id: 33210, path: "/m/search", incoming_domain_id: 4>,
 #<IncomingReferer:0x0000557eaabe2908 id: 44231, path: "/m/search", incoming_domain_id: 4>,
 #<IncomingReferer:0x0000557eaabe27c8 id: 42228, path: "/m/search", incoming_domain_id: 26>]

Eu simplesmente eliminaria todas as linhas duplicadas… há pouco valor nessas informações.

Com prazer. Você pode fornecer o comando correto? Não estou familiarizado especificamente com o PostgreSQL, mas conheço SQL.

Que bom ouvir isso. Tenho atualizado com muito esforço a outra tabela que referencia essas linhas. É um grande incômodo, pois nunca consigo lembrar o que era, então é sempre a primeira vez, repetidamente.

IncomingReferer.find(44231).destroy
IncomingReferer.find(42228).destroy

Remover esses dois duplicados foi bem-sucedido, mas a reconstrução subsequente dos índices gerou novos erros. Isso é um problema grave? Como corrigimos ou deletamos aquela linha de busca 3433?

[1] pry(main)> IncomingReferer.find(44231).destroy
=> #<IncomingReferer:0x000055734c65d8e8 id: 44231, path: "/m/search", incoming_domain_id: 4>
[2] pry(main)> IncomingReferer.find(42228).destroy
=> #<IncomingReferer:0x000055734cd81a70 id: 42228, path: "/m/search", incoming_domain_id: 26>
postgres=# \connect discourse
Você agora está conectado ao banco de dados "discourse" como usuário "postgres".
discourse=# REINDEX SCHEMA CONCURRENTLY public;
AVISO:  não é possível reindexar o índice inválido "public.incoming_referers_pkey_ccnew" de forma concorrente, ignorando
AVISO:  não é possível reindexar o índice inválido "public.index_incoming_referers_on_path_and_incoming_domain_id_ccnew" de forma concorrente, ignorando
AVISO:  não é possível reindexar o índice inválido "pg_toast.pg_toast_2782645_index_ccnew" de forma concorrente, ignorando
ERRO:  não foi possível criar o índice exclusivo "index_incoming_referers_on_path_and_incoming_domain_id_ccnew1"
DETALHE:  A chave (path, incoming_domain_id)=(/search/, 3433) está duplicada.
CONTEXTO:  worker paralelo

Aqui está o código que trata a criação… isso deveria estar lidando corretamente, mas poderíamos atualizá-lo para um insert ON CONFLICT se necessário?

Tentei reconstruir manualmente esses 4 índices. Dois tiveram sucesso, dois falharam. Devo eliminar essas duas linhas duplicadas?

discourse=# REINDEX INDEX CONCURRENTLY "public"."incoming_referers_pkey_ccnew";
REINDEX
discourse=# REINDEX INDEX CONCURRENTLY "public"."index_incoming_referers_on_path_and_incoming_domain_id_ccnew";
ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_cc_ccnew"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 1861) is duplicated.
discourse=# REINDEX INDEX CONCURRENTLY "pg_toast"."pg_toast_2782645_index_ccnew";
REINDEX
discourse=# REINDEX INDEX CONCURRENTLY "index_incoming_referers_on_path_and_incoming_domain_id_ccnew1";
ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_c_ccnew1"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 1905) is duplicated.

Sim, por favor, elimine as linhas duplicadas.

@riking Corromper índices no PostgreSQL é um bug do PostgreSQL, não do Discourse. Podemos certamente melhorar o desempenho dessa inserção, mas o bug do PostgreSQL precisa ser corrigido no próprio PostgreSQL.

Minha suposição é que isso tenha a ver com algum tipo de desligamento abrupto do motor do banco de dados, talvez devido a uma queda de energia.

Essa é uma explicação razoável. O comando ./launcher shutdown app (ou rebuild) realiza um desligamento limpo do PostgreSQL de alguma forma? Ah, mas aposto que uma atualização não supervisionada não sabe como fazer um desligamento limpo dos contêineres do Docker, certo?