Falha na atualização 2.7.0.beta2 com ERRO: chave duplicada

Estava atualizando a partir da versão 2.7.0.beta1 e recebi esta mensagem de erro:

2021-01-22 20:16:22.015 UTC [4055] discourse@discourse LOG:  duração: 75335.241 ms  instrução: UPDATE notifications SET processed = true
2021-01-22 20:16:23.792 UTC [4055] discourse@discourse LOG:  duração: 1776.591 ms  instrução: ALTER TABLE "notifications" ALTER COLUMN "processed" SET NOT NULL
2021-01-22 20:16:25.198 UTC [4055] discourse@discourse LOG:  duração: 1323.298 ms  instrução: CREATE  INDEX  "index_notifications_on_processed" ON "notifications"  ("processed")
2021-01-22 20:16:25.458 UTC [4055] discourse@discourse LOG:  duração: 241.063 ms  instrução: CREATE TABLE "user_notification_schedules" ("id" bigserial primary key, "user_id" integer NOT NULL, "enabled" boolean DEFAULT FALSE NOT NULL, "day_0_start_time" integer NOT NULL, "day_0_end_time" integer NOT NULL, "day_1_start_time" integer NOT NULL, "day_1_end_time" integer NOT NULL, "day_2_start_time" integer NOT NULL, "day_2_end_time" integer NOT NULL, "day_3_start_time" integer NOT NULL, "day_3_end_time" integer NOT NULL, "day_4_start_time" integer NOT NULL, "day_4_end_time" integer NOT NULL, "day_5_start_time" integer NOT NULL, "day_5_end_time" integer NOT NULL, "day_6_start_time" integer NOT NULL, "day_6_end_time" integer NOT NULL)
2021-01-22 20:16:25.560 UTC [4055] discourse@discourse LOG:  duração: 100.868 ms  instrução: CREATE  INDEX  "index_user_notification_schedules_on_user_id" ON "user_notification_schedules"  ("user_id")
2021-01-22 20:16:25.782 UTC [4055] discourse@discourse LOG:  duração: 142.180 ms  instrução: CREATE  INDEX  "index_do_not_disturb_timings_on_scheduled" ON "do_not_disturb_timings"  ("scheduled")
2021-01-22 20:16:26.414 UTC [4055] discourse@discourse LOG:  duração: 361.514 ms  instrução: UPDATE users
	SET locale = 'en_GB'
	WHERE locale = 'en'
	
2021-01-22 20:16:26.656 UTC [4055] discourse@discourse LOG:  duração: 132.778 ms  instrução: UPDATE theme_translation_overrides
	SET locale = 'en_GB'
	WHERE locale = 'en'
	
2021-01-22 20:16:42.745 UTC [4055] discourse@discourse ERROR:  valor de chave duplicada viola a restrição de unicidade "index_users_on_username"
2021-01-22 20:16:42.745 UTC [4055] discourse@discourse DETAIL:  Chave (username)=(DaveW) já existe.
2021-01-22 20:16:42.745 UTC [4055] discourse@discourse STATEMENT:  UPDATE users
	SET locale = 'en'
	WHERE locale = 'en_US'
	
rake aborted!
StandardError: Ocorreu um erro; esta e todas as migrações subsequentes foram canceladas:

PG::UniqueViolation: ERROR:  valor de chave duplicada viola a restrição de unicidade "index_users_on_username"
DETAIL:  Chave (username)=(DaveW) já existe.

E então, no final da saída:

I, [2021-01-22T20:16:42.805286 #1] INFO -- : Encerrando processos assíncronos

I, [2021-01-22T20:16:42.805333 #1] INFO -- : Enviando INT para HOME=/var/lib/postgresql USER=postgres exec chpst -u postgres:postgres:ssl-cert -U postgres:postgres:ssl-cert /usr/lib/postgresql/13/bin/postmaster -D /etc/postgresql/13/main pid: 49

I, [2021-01-22T20:16:42.805381 #1] INFO -- : Enviando TERM para exec chpst -u redis -U redis /usr/bin/redis-server /etc/redis/redis.conf pid: 166

166:signal-handler (1611346602) Recebeu SIGTERM agendando desligamento...

2021-01-22 20:16:42.805 UTC [49] LOG: recebido pedido de desligamento rápido

2021-01-22 20:16:42.835 UTC [49] LOG: abortando todas as transações ativas

2021-01-22 20:16:42.857 UTC [49] LOG: worker de fundo "iniciador de replicação lógica" (PID 58) saiu com código de saída 1

166:M 22 Jan 2021 20:16:42.876 # Usuário solicitou desligamento...

166:M 22 Jan 2021 20:16:42.876 * Salvando o snapshot RDB final antes de sair.

166:M 22 Jan 2021 20:16:44.758 * Banco de dados salvo no disco

166:M 22 Jan 2021 20:16:44.758 # Redis está pronto para sair, tchau tchau...

2021-01-22 20:16:45.563 UTC [53] LOG: desligando

I, [2021-01-22T20:16:52.806177 #1] INFO -- : HOME=/var/lib/postgresql USER=postgres exec chpst -u postgres:postgres:ssl-cert -U postgres:postgres:ssl-cert /usr/lib/postgresql/13/bin/postmaster -D /etc/postgresql/13/main pid:49 não terminou corretamente, forçando o término!

FALHA

--------------------

Pups::ExecError: cd /var/www/discourse && su discourse -c 'bundle exec rake db:migrate' falhou com retorno #<Process::Status: pid 4032 exit 1>

Localização da falha: /pups/lib/pups/exec_command.rb:112:in `spawn'

exec falhou com os parâmetros {"cd"=>"$home", "hook"=>"db_migrate", "cmd"=>["su discourse -c 'bundle exec rake db:migrate'"]}

d627ad17d1f22d839a7dc8099878e6272eb3ea1772539f6628e2a23dd830aca2

** FALHA NO BOOTSTRAP ** por favor, role para cima e procure mensagens de erro anteriores; pode haver mais de uma.

./discourse-doctor pode ajudar a diagnosticar o problema.

Não faço ideia de como poderia haver duas contas com o mesmo nome de usuário.

Nosso site está totalmente fora do ar. O que devo fazer agora?

Obrigado,
Gunnar

2 curtidas

Acho que você pode trazer o site de volta com ./launcher start app

Em seguida, verifique as formas de excluir o duplicado:

e

5 curtidas

Mais informações que eu provavelmente deveria fornecer:

  • Esta é uma instalação de único container.
  • A primeira reconstrução foi concluída sem erros. Esse erro ocorreu quando eu estava executando a segunda reconstrução, após a primeira ter sido concluída com sucesso.

Obrigado,
Gunnar

1 curtida

Você tem espaço suficiente no disco? Erro relacionado à necessária atualização do PostgreSQL para a versão 13, pelo que sei. Você seguiu este método de atualização?

cd /var/discourse
git pull
 ./launcher rebuild app

Se você pesquisar no fórum por esses erros, verá muitas informações passadas e possíveis correções.

Tente isso.

Pesquise no fórum pelos erros que você postou para ver várias correções que funcionaram para outras pessoas.

1 curtida

Consegui colocar o site de volta no ar renomeando postgres_data_old de volta para postgres_data e, em seguida, executando ./launcher start app para iniciar a imagem antiga.

Então, estou de volta ao ponto de partida, mas pelo menos o site está no ar.

Tentei encontrar a chave duplicada executando o seguinte:

select * from users WHERE username = 'DaveW';

(O username = DaveW era a chave problemática, conforme mostrado na mensagem de erro no meu primeiro post neste tópico.)

O comando retornou apenas uma linha, um único usuário. O que estou deixando passar?

Obrigado,
Gunnar

1 curtida

Você tem dois, possivelmente com capitalização diferente no banco de dados:

1 curtida

Você visitou meu site? :wink:

Entendo o que você quer dizer. Quando procuro na GUI, vejo dois. No entanto, ao clicar em ambos como administrador e abrir a página de detalhes, eles parecem ser o mesmo usuário. Não consigo encontrar nenhuma diferença entre os dois. Basicamente, não importa qual dos dois eu clique, recebo os dados do segundo, aquele com o nome completo preenchido.

Como esse usuário nunca havia postado e não visitava o site há algum tempo, excluí a conta pela GUI.

Curiosamente, ainda tenho o outro, o primeiro da sua lista, sem nome completo. Mas agora, se eu clicar nesse usuário, nada acontece. Vejo uma tentativa de abrir uma janela de diálogo, que fecha imediatamente.

Ao pesquisar diretamente no banco de dados por username = DaveW, agora retornam zero linhas. No entanto, se eu pesquisar por

select * from users WHERE name = 'DaveW';

(name, não username), recebo 1 linha retornada:

 19732 | DaveW    | 2016-11-15 12:43:02.708166 | 2016-11-15 12:43:02.708166 | DaveW |                    0 |                |               |      | t      | davew          | 2016-11-15 12:43:02.708166 | f     | 2017-06-01 18:09:45.018058 |           1 | f        |                |             |                   |              |                |               |     0 |          0 |            | f         |       |                    |        |                  |                         | f      |               |               |                          |                           | 
(1 linha)

Perceba como ele tem DaveW (mesma grafia) no campo username! Essa conta também é três anos mais antiga que a outra.

Posso me livrar dela com:

DELETE from users WHERE id = 19732;

sem nenhum efeito colateral?

Obrigado por toda a ajuda!

Gunnar

1 curtida

Claro! :grinning:

Isso deve excluir o 19732, mas não posso garantir a ausência de quaisquer efeitos adversos. De qualquer forma, é recomendável fazer um backup antes de prosseguir.

3 curtidas

Você também pode alterar o nome de usuário de um dos duplicados.

2 curtidas

Certo.

Depois que eu fizer isso, devo ser capaz de reiniciar a atualização do zero? Ou seja, executar o rebuild duas vezes?

Obrigado!
Gunnar

4 curtidas

Isso deve funcionar. A menos que existam outros usuários duplicados.

2 curtidas

Acontece que existem, pelo menos um.

PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "index_users_on_username_lower"
DETAIL:  Key (username_lower)=(robs) already exists.

Existe alguma maneira de eu escanear o banco de dados em busca de duplicatas? Não apenas usuários, mas qualquer tabela que possa causar esse erro durante a atualização?

Gostaria de encontrá-los e corrigi-los todos antes de tentar novamente.

Além disso, seria bom que isso fizesse parte do processo de atualização. Talvez executar uma verificação primeiro e interromper o processo antes que qualquer alteração seja feita. Avisar o usuário e fornecer uma lista das duplicatas, com um link para uma página aqui no meta com instruções sobre como corrigir?

2 curtidas

Uma abordagem menos invasiva pode ser reindexar o banco de dados antes de atualizar, o que deve indicar quais precisam ser corrigidos.

2 curtidas

Não sou especialista em banco de dados, então isso parece assustador. E, se entendi corretamente, você acabaria com índices duplicados e teria que excluí-los manualmente?

Além disso, aqui está a parte estranha. Como você descobriu no meu site com a duplicata anterior, agora encontrei um usuário duplicado chamado RobS na interface gráfica. Mas, assim como antes, não importa qual deles eu clique, sempre termino na página de perfil de apenas um deles. Ou seja:

  • Listar usuários chamados RobS na interface gráfica. Encontrar dois. Ver que ambos têm estatísticas diferentes.
  • Clicar no usuário #1. Ver perfil e estatísticas pertencentes ao usuário #2.
  • Clicar no usuário #2. Ver perfil e estatísticas pertencentes ao usuário #2.

select * from users WHERE username_lower = 'robs';

Retorna 1 linha: O que parece ser o usuário #2 (as datas batem).

select * from users WHERE username = 'RobS';

Também retorna apenas 1 linha: O que parece ser o usuário #1 (novamente, as datas batem). Este usuário tem um ID diferente do outro.

Pelo que se vê na saída, ambos tinham o mesmo username e username_lower, mas apenas 1 linha foi retornada para cada comando SELECT. Meu banco de dados está em sérios problemas?

Será que uma reindexação resolveria isso?

2 curtidas

Tentei reconstruir novamente e encontrei outro duplicado com o nome de usuário “drc”. Na interface gráfica, encontrei estes dois:

Deborah C
David C

Tente clicar em qualquer um dos usuários para ver os detalhes no “cartão” do perfil deles. Você verá que, não importa qual deles você clique, os detalhes exibidos serão os de Deborah C.

Não tenho ideia de como isso aconteceu, mas qual é a melhor ação agora? Desisti da atualização até conseguir resolver isso.

Como eu reindexaria? Assim?

REINDEX SCHEMA CONCURRENTLY public;

Isso me diria quais chaves estão duplicadas?

1 curtida

Quero saber o que o seguinte retornaria:

select id, username FROM users WHERE username_lower = 'robs' OR username = 'RobS':

Não seria algo bobo, como uma das entradas de username ter espaços em branco ou algum caractere não imprimível/Unicode de alguma forma?

1 curtida

Acho que sim, e não sei se há uma maneira melhor de fazer isso. Na minha instância de teste, ele simplesmente executa sem nenhuma saída. Quando atualizei meu site de produção, havia apenas uma duplicata para lidar, então minha experiência pode não ser um bom exemplo para a sua situação. De qualquer forma, faça um backup antes de operações no banco de dados—geralmente crio um snapshot no DigitalOcean para que a restauração, em caso de problemas, seja rápida e fácil.

1 curtida

Acho que foi assim que resolvi casos semelhantes no passado. Reconstrua o índice; quando falhar, ele mostrará o item duplicado. Corrija-o e tente novamente. Repita o processo.

2 curtidas

Obrigado. Quando ele falhar, como eu encontro e elimino o novo índice incompleto? É tão simples quanto adicionar “_ccnew” ao nome da tabela que falhou?

Ou seja, quando ele trava em “users” por causa de um duplicado, eu primeiro corrijo o duplicado e depois:

DROP index ‘users_ccnew’;

Depois, repito o processo? É tão simples quanto isso?

Obrigado,
Gunnar

1 curtida

Ah, sim. Estou agora na situação de ter encontrado dois usuários válidos (e ativos) com o mesmo nome de usuário e username_lower:

  id   | username | (redacted) | username_lower |
 42379 | DrC      | (redacted) | drc            |
 47695 | DRC      | (redacted) | drc            |

Parece que preciso alterar tanto o nome de usuário quanto o username_lower do segundo usuário. Como faria isso no psql?

Obrigado!
Gunnar

1 curtida