Hoje tentei atualizar minha instalação do Discourse, de 2.9.0.beta9 para 2.9.0.beta10. Mas isso deu terrivelmente errado.
Minha primeira tentativa foi no console
cd /var/discourse
sudo git pull
sudo ./launcher rebuild app
Isso finalmente falhou, com a seguinte mensagem em algum lugar no meio de todos os logs
PG::InvalidParameterValue: ERROR: cannot extract elements from a scalar
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/pg.rb:110:in `exec'
Consegui simplesmente restaurar o fórum iniciando a imagem antiga (que ainda estava por perto).
Minha segunda tentativa foi através do console web (/admin/upgrade). Mas isso finalmente engasgou com o mesmo tipo de erro: ERROR: cannot extract elements from a scalar. Ao voltar para o painel de atualização, ele me disse que todos os componentes foram atualizados com sucesso. Mas após uma reinicialização do contêiner, o servidor agora lançou um erro HTTP 500
Fiz uma instalação limpa em uma máquina separada, começando do zero. Consegui instalar a versão beta10, mas tentar restaurar de um backup causou exatamente o mesmo erro!
INSERT INTO question_answer_votes (post_id, user_id, created_at)
SELECT
X.post_id AS post_id,
(X.value->>'user_id')::int AS user_id,
(X.value->>'created_at')::timestamp AS created_at
FROM (
SELECT
post_id,
jsonb_array_elements(value::jsonb) AS value
FROM post_custom_fields WHERE name = 'vote_history'
) AS X
WHERE (X.value->>'action') != 'destroy'
ORDER BY (X.value->>'created_at')::timestamp DESC
ON CONFLICT DO NOTHING
Se eu entendi corretamente, isso acontece porque a função jsonb_array_elements do psql espera um array, mas recebe um valor NULL.
O site que vi tinha um monte desses Post Custom Fields que continham um monte de strings que haviam sido codificadas várias vezes, de modo que se tornaram inutilizáveis.
Algumas soluções, em ordem de complexidade:
parar de usar o plugin (e talvez mudar para os novos up votes)
excluí-los todos
excluir os ruins
editar esses campos para transformar os dados de volta em strings json válidas.
O site que vi tinha dados ruins com anos de idade. Isso parece ser um bug que existia anos atrás e que só agora está sendo descoberto.
Pode ser possível escrever código para corrigir as strings json quebradas, mas não consegui ver como fazer isso em 10 minutos.
Isso parece contaminação de dados de posts antigos de Perguntas e Respostas, como você disse. Eu também acho engraçado que isso esteja aparecendo agora, dado que essa migração é de novembro de 2021 – suponho que seja porque JayJay só está mudando para o novo plugin agora? De qualquer forma, vamos dar uma olhada nessa migração novamente.
Se eu removesse o plugin, restaurar o backup ainda executaria a consulta problemática.
Portanto, eu precisaria remover tanto o plugin quanto qualquer tabela + consulta usada por este plugin.
Como eu saberia as tabelas envolvidas?
Vou tentar isso agora. Eu examinei o arquivo dump.sql, que faz parte do backup, e não há menção de nenhuma tabela question_answer_* de forma alguma. Então isso me dá esperança…
Os dados corrompidos estão na tabela PostCustmField. Mas se você não tiver o plugin, ele não tentará migrar esses dados para a nova tabela.
O problema não é a migração em si, é que em algum momento no passado os dados foram corrompidos. Ele esteve quebrado, mas quebrou de uma forma que passou despercebida por anos. Acho que talvez cada novo upvote o tenha corrompido ainda mais. Uma correção razoável pode ser ignorar os dados corrompidos, talvez marcando-os como corrompidos (talvez renomeando o campo personalizado) para que migrações futuras possam ignorá-los e alguém possa corrigi-los manualmente, se assim desejar.
Sucesso em produção também. Desabilitei o plugin e seguimos em frente.
Ainda me pergunto por que não encontrei esse problema antes. Como eu disse, todos os meses, como rotina, atualizamos todos os nossos sistemas, então eu deveria ter visto esse problema mais cedo.
Como eu seria capaz de me conectar ao banco de dados do Discourse sozinho para verificar o que está na tabela post_custom_fields?
O antigo plugin QnA usava conversão de tipo de campo personalizado padrão do Discourse, que serve para lidar com a conversão desse campo para JSON. Mas isso simplesmente não funciona em alguns casos (como este), e é por isso que você (idealmente) precisa criar verificações de formato ao trabalhar com campos personalizados do Discourse (particularmente se você estiver trabalhando com código e dados bastante antigos como este). Eu sugiro que é isso que o Plugin Upvotes precisa fazer aqui, que de fato é de onde vem o erro imediato.
Você pode usar o plugin data explorer para verificar o que você tem lá.
Bem, eu concordo com você na maior parte, e acho que realmente concordamos. Acho que é verdade que a migração funciona se os dados não estiverem corrompidos. Concordamos que, se os dados estiverem corrompidos, isso deve falhar de forma mais graciosa do que agora.
Sim, mas os dados provavelmente foram corrompidos anos atrás (esse é o caso no site que conheço), mas você não percebeu porque não falhou catastroficamente. Tenho certeza de que não estava gerenciando os votos como esperado, mas ninguém percebeu.
Eu faria isso a partir do rails, algo como isto:
./launcher enter app
rails c
Então, coisas como esta:
all_votes=PostCustomField.where(name: "vote_history")
likely_broken_votes=PostCustomField.where(name: "vote_history").where("value like '\\\"%'")
Olhe apenas para o id e os dados:
all_votes.pluck(:id,:value)
Obtenha apenas um pcf:
pcf=PostCustomField.find(1234)
Corrija isso
pcf.value='o conteúdo que você realmente quer nele'
pcf.save
Algumas pessoas têm uma versão muito antiga do plugin de perguntas e respostas com meu nome de usuário pessoal do GitHub na URL do repositório em seu arquivo app.yml.
Transfiri o plugin QnA para paviliondev anos atrás. O Github redireciona URLs de repositório quando eles são transferidos, então as URLs antigas com meu nome de usuário pessoal continuaram funcionando.
Mais anos depois, o Pavilion transferiu o Plugin de Perguntas e Respostas para o Discourse. O Discourse inicialmente manteve o nome discourse-question-answer.
Há alguns meses, criei meu próprio fork do plugin hospedado em discourse, enquanto ele ainda era chamado discourse-question-answer.
Pessoas com links muito antigos para o plugin QnA com minha conta pessoal do GitHub nelas estavam agora clonando meu novo fork do discourse-question-answer significativamente atualizado, hospedado em discourse. Em outras palavras, um link muito antigo agora apontava para um fork de um novo plugin. Apesar dos anos que se passaram, eu deveria ter previsto isso, então peço desculpas por isso. Removi esse fork.
O Discourse mudou o nome de discourse-question-answer para discourse-upvotes. Essa mudança de nome não teve um impacto material no seu caso @Jaap-Jan_Swijnenburg, mas é por isso que você agora está (inesperadamente) clonando um fork desse repositório.
Uma migração em discourse/discourse-upvotes (ex discourse-question-answer) assume que a coluna value em post_custom_fields é do tipo JSON.
Dados antigos criados pelo plugin quando era angusmcleod/discourse-question-answer (anos atrás) não foram salvos como JSON válido pela preocupação HasCustomFields em discourse/discourse. Estou adivinhando, mas esses dados provavelmente foram adicionados antes que a verificação de tipo JSON fosse adicionada há 4 anos (Ainda é possível acabar com JSON inválido em campos personalizados registrados como JSON em casos extremos).
Portanto
Quando pessoas com a URL (desatualizada há anos) angusmcleod/discourse-question-answer em seu app.yml atualizam seu Discourse, a migração na nova versão do plugin é clonada, a migração é executada e potencialmente cria esse erro.
Existem algumas soluções para isso:
@Jaap-Jan_Swijnenburg, no seu caso, você só precisa remover a referência ao meu antigo plugin QnA e poderá reconstruir seu site. É só isso; nada mais. Parece que foi o que você fez
A migração do plugin discourse/discourse-upvotes poderia ser atualizada para lidar com valores não JSON na coluna value em post_custom_fields.
Observo que 2 também lidaria com o caso adicional de pessoas que realmente desejam mudar de uma versão antiga do plugin QnA para discourse-upvotes. Nesse caso, a migração será executada e falhará se alguma entrada na coluna value de post_custom_fields não for um JSON válido.