Problema estranho de codificação na página de categorias

Estou tentando rastrear um problema estranho em uma instalação não dockerizada (percebo que o suporte para este tipo de instalação é limitado/inexistente, então estou apenas procurando algumas dicas sobre o que pode estar errado aqui - nossa equipe de empacotamento interna usou as instruções de ‘build de desenvolvedor’ para descobrir como construir os pacotes necessários). Consegui confirmar que o problema é específico da forma como instalamos - minha equipe de infraestrutura não está disposta a usar uma instalação dockerizada (eles preferem construir tudo sozinhos), então executo instâncias sandbox que são dockerizadas e não dockerizadas com uma cópia do nosso banco de dados para verificar onde está um problema, e isso é definitivamente um artefato da forma como instalamos nossa configuração.

Ao atualizar de 3.3.2 para 3.3.3, alguns de nossos funcionários do fórum não falantes de inglês notaram que o texto “sobre” para seções que usam caracteres acentuados não está codificado corretamente:

Curiosamente, podemos ver que o título, bem como todo o outro texto, está codificado corretamente. Na verdade, a própria mensagem usada para a mensagem “sobre” está codificada corretamente:

Confirmei que este é o mesmo texto editando-o e vendo a alteração na página de categorias.

Portanto, é algo específico da renderização desse texto na página de categorias.

Olhando para document.characterSet no meu navegador, ele é identificado corretamente como UTF-8. O banco de dados também mostra o formato como UTF-8.

Estou me perguntando se alguém pode me indicar o que é diferente na forma como esse texto é renderizado na página de categorias. Minha suposição é que é algum pacote ruby que não foi construído corretamente (talvez faltando suporte a UTF-8) que é usado na renderização desse texto, mas não em outro texto no sistema, ou algo que processa o texto da mensagem “sobre” e o trunca (o que notei que é o caso aqui; no entanto, também temos um link para um fórum francês externo que é uma mensagem não truncada, mas suponho que ainda seja avaliado pelo mesmo código).

Obrigado por qualquer dica. Estou um pouco perplexo aqui.

Vejo que às vezes está correto:

Puxar um categories.json bruto mostra que está errado apenas no trecho:

        "description": "Esta sección del Foro se dedica a las personas usuarias de openSUSE que forman parte de la comunidad lingüística castellana, de tal forma que dichas personas puedan consultar y participar en el foro en dicha lengua (sea el dialecto español o cualquiera de las variedades latinoamericanas, etc.).",
        "description_text": "Esta sección del Foro se dedica a las personas usuarias de openSUSE que forman parte de la comunidad lingüística castellana, de tal forma que dichas personas puedan consultar y participar en el foro en dicha lengua (sea el dialecto español o cualquiera de las variedades latinoamericanas, etc.).",
        "description_excerpt": "Esta sección del Foro se dedica a las personas usuarias de openSUSE que forman parte de la comunidad lingüística castellana, de tal forma que dichas personas puedan consultar y participar en el foro en dicha lengua (sea el dialecto español o cualquiera de las variedades latinoamericanas, etc.).",

Criar a mesma categoria em try.discourse.org e verificar categories.json dá o resultado correto:

        "description": "Esta sección del Foro se dedica a las personas usuarias de openSUSE que forman parte de la comunidad lingüística castellana, de tal forma que dichas personas puedan consultar y participar en el foro en dicha lengua (sea el dialecto español o cualquiera de las variedades latinoamericanas, etc.).",
        "description_text": "Esta sección del Foro se dedica a las personas usuarias de openSUSE que forman parte de la comunidad lingüística castellana, de tal forma que dichas personas puedan consultar y participar en el foro en dicha lengua (sea el dialecto español o cualquiera de las variedades latinoamericanas, etc.).",
        "description_excerpt": "Esta sección del Foro se dedica a las personas usuarias de openSUSE que forman parte de la comunidad lingüística castellana, de tal forma que dichas personas puedan consultar y participar en el foro en dicha lengua (sea el dialecto español o cualquiera de las variedades latinoamericanas, etc.).",

Não tenho certeza qual seria o próximo passo para rastrear isso em sua instalação, mas talvez focar no caminho do código que gera o trecho ajude, além de saber que isso surgiu de algo interpretando a codificação UTF-8 como iso-8859-1.

Sim, essa é a minha suposição - o que quer que esteja gerando o trecho é provavelmente o lugar certo. Só não tenho certeza de onde isso está no código em si. Mas saber para procurar o termo “excerpt” é definitivamente útil - obrigado!

Pareceu-me que estava sendo recebido como iso-8859-1 em algum momento, então agradeço essa confirmação também (eu não tinha 100% de certeza de que era a codificação errada que eu estava vendo, mas parecia certo).

O que você viu em try.discourse.org foi o que eu vi em minha instalação dockerizada também (bem, o resultado final da codificação correta :slight_smile: )

Obrigado!

Você pode verificar facilmente com:

○ → ipython3

In [1]: 'Esta sección del Foro se dedica a las personas usuarias de openSUSE que forman parte de la comunidad lingüística castellana
   ...: , de tal forma que dichas personas puedan consultar y participar en el foro en dicha lengua (sea el dialecto español o cualq
   ...: uiera de las variedades latinoamericanas, etc.).'.encode('utf-8').decode('iso-8859-1')
Out[1]: 'Esta sección del Foro se dedica a las personas usuarias de openSUSE que forman parte de la comunidad lingüÃ\xadstica castellana, de tal forma que dichas personas puedan consultar y participar en el foro en dicha lengua (sea el dialecto español o cualquiera de las variedades latinoamericanas, etc.).'

Minha suposição é que algo está fazendo algum tipo de cache. Isso não ajuda muito, mas é o que eu tentaria procurar.

A menos que eles simplesmente odeiem o Docker, eles podem criar suas próprias imagens com discourse_docker. Então eles podem ver exatamente o que está acontecendo e não ter que confiar nas imagens de mais ninguém.

Legal, obrigado por isso.

Pensei que poderia ser o caso, mas mudar a mensagem resultou em uma atualização, então não acho que seja cache - é algo que está codificando incorretamente.

Sugeri algumas opções, mas no final, a equipe de infra optou por usar pacotes construídos com o serviço de compilação. Não acho que seja uma questão de “odiamos o Docker” (embora o podman provavelmente seja mais o que eles gostariam de usar), mas sim uma forma de usar as ferramentas de gerenciamento de configuração que eles estão usando para gerenciar tudo da mesma maneira. Ter algo único que usa Docker/podman adicionaria outras complexidades ao uso da configuração de CI/CD que eles estão usando (ou pelo menos é o que eu entendo).

Portanto, no final, configurei minhas duas sandboxes para poder determinar onde os problemas estão enraizados para que eu pudesse relatá-los ao local apropriado; infelizmente, isso significa que, quando se trata de algo na forma como construímos as coisas, tenho que investigar o que estamos fazendo de diferente de uma instalação padrão baseada em Docker para que possamos corrigi-lo.

Eu entendo como eles pensam que a maneira do Discourse é louca e eles realmente querem que tudo seja gerenciado sob um Sistema Unificado.

Mas. O último cliente que tive que insistiu em usar suas ferramentas favoritas acabou me pagando quase 20 horas de trabalho para obter um backup utilizável para migrar para a hospedagem do discourse.org. O anterior pagou bem mais para ajustar sua configuração personalizada para evitar que o site travasse várias vezes por semana, e um ano depois eles me pagaram ainda mais para migrá-los para a hospedagem do discourse.org. :slight_smile:

Boa sorte!

3 curtidas

Agradeço o conselho. A boa notícia é que os backups do nosso sistema de produção funcionam em uma instalação dockerizada sem problemas (eu testei isso), então, se/quando eles decidirem que usar uma instalação baseada em Docker é o caminho certo a seguir, estaremos em boa situação. Temos muitos dados (migrados do vBulletin há alguns anos para o Discourse), e, em geral, as coisas funcionaram muito bem, com alguns soluços estranhos aqui e ali.

Consequentemente, aprendemos muito sobre como o Discourse funciona, então não é uma coisa ruim em geral. :slight_smile:

Parece que /categories.json é um endpoint de API em vez de um arquivo estático que é criado e depois lido, então acho que isso ajuda a limitar o problema a ruby ou javascript. Encontrei onde está o esquema para este endpoint, mas, não sendo particularmente familiarizado com ruby (acumulei muita experiência em linguagens de programação ao longo dos anos, então ler a maioria das linguagens não é um problema para mim, mesmo que eu não possa codificar nelas - consigo entender a ideia com bastante facilidade), mas parece que o javascript é executado principalmente no navegador, e o ruby é executado no servidor (embora eu note que o nodejs também está instalado, então essa generalização pode não se sustentar realmente).

Se eu conseguir encontrar a função que processa /categories (já que parece que .json no final apenas diz ao código como formatar a saída; vejo um comportamento semelhante em /top vs /top.rss, por exemplo), isso deve limitar onde preciso procurar no código, e isso me dirá quais gems ruby (tenho quase certeza de que será código ruby) precisam ser verificados para garantir que foram compilados corretamente.

2 curtidas

Parece ser algo específico das funções de extração - acabei de notar que isso também acontece em nossas páginas de resultados de pesquisa:

(por exemplo)

O texto é:

Não vejo um botão “Compartilhar”.

Que é algo que eu mesmo citei em uma resposta a um usuário (panorain). Aconteceu por engano, pois ao tentar ver minha própria atividade, recebo um erro de servidor 500, e a saída de /logs mostra um erro de tempo de execução “a string de entrada não pode estar vazia” em lib/excerpt_parser.rb.

Parece que algumas coisas estão levando de volta a algo no processamento de extração, mas apenas nas instalações em estilo de desenvolvimento.

Na minha instalação baseada em docker, consigo visualizar minha atividade sem erros; estranhamente, porém, o banco de dados dessa instalação é restaurado de um backup recente de um servidor de produção - onde o problema existe.

2 curtidas

Parece que atualizamos o nokogiri para 1.17.2, e vejo que a versão Dockerizada é 1.16.7 - suspeito que essa seja a causa desse problema. Vou tentar reverter essa atualização (e qualquer outra coisa que tenha sido atualizada ao mesmo tempo).

Então eu fiz o downgrade do nosso pacote para usar nokogiri 1.16 novamente. O que eu não entendo. Sempre que eu atualizo uma gem para reduzir a duplicação de empacotamento, eu verifico se houve mudanças relacionadas em main, não houve nenhuma. A menos que eu tenha perdido alguma coisa

        "description": "Witaj w polskiej sekcji społeczności openSUSE!",
        "description_text": "Witaj w polskiej sekcji społeczności openSUSE!",
        "description_excerpt": "Witaj w polskiej sekcji społeczności openSUSE!",

como você pode ver, temos o texto correto duas vezes, apenas quando ele passa por PrettyText.excerpt ele está quebrado. Como isso é tratado em main?

@hendersj Já estou preparando um pacote main para que possamos testar isso com uma cópia do banco de dados.

Eu acho que foi tratado em DEV: Update nokogiri to 1.18.1 (#30554) · discourse/discourse@affe26f · GitHub

mas eu me pergunto… em lib/retrieve_title.rb

doc = Nokogiri.HTML5(html, encoding:)

não deveria ser:

doc = Nokogiri.HTML5(html, encoding: Encoding::UTF_8)
1 curtida

@pfaffman esse código estranho também está na versão 3.4.0. :slight_smile:

você poderia verificar se isso realmente deve ser chamado com uma configuração de codificação vazia?

Alguma razão para você pensar assim? UTF-8 é o padrão.

[1] pry(main)> Nokogiri::VERSION
=> "1.18.2"

[2] pry(main)> t = '<div>Witaj w polskiej sekcji społeczności openSUSE!</div>'
=> "<div>Witaj w polskiej sekcji społeczności openSUSE!</div>"

[3] pry(main)> Nokogiri.HTML5(t).to_s
=> "<html><head></head><body><div>Witaj w polskiej sekcji społeczności openSUSE!</div></body></html>"

[4] pry(main)> Nokogiri.HTML5(t, encoding: Encoding::UTF_8).to_s
=> "<html><head></head><body><div>Witaj w polskiej sekcji społeczności openSUSE!</div></body></html>"

[5] pry(main)> Nokogiri.HTML5(t).to_s == Nokogiri.HTML5(t, encoding: Encoding::UTF_8).to_s
=> true

A função retrieve_title é usada para extrair títulos de URLs externos (por exemplo, Youtube) e, embora eu não esteja intimamente familiarizado com esse caminho de código, ficaria surpreso em descobrir que essa é a origem do seu problema.

Se você estiver fazendo algo diferente (por exemplo, usando esta função em um plugin personalizado), o parâmetro de codificação lá vem do cabeçalho content-type do recurso buscado:

        if !encoding && content_type = _response["content-type"]&.strip&.downcase
          if content_type =~ /charset="?([a-z0-9_-]+)"?/
            encoding = Regexp.last_match(1)
            encoding = nil if !Encoding.list.map(&:name).map(&:downcase).include?(encoding)
          end
        end

        max_size = max_chunk_size(uri) * 1024
        title = extract_title(current, encoding)

então se suspeitaria que o servidor web respondente está relatando um content-type incorreto.

porque todas as outras chamadas nesse patch têm encoding: parameters especificado.

apenas a em retrieve title não tem, o que parece inconsistente. e não lidar corretamente com a codificação UTF-8 foi toda a discussão que levou a este tópico.

Ah:

é uma abreviação para:

doc = Nokogiri.HTML5(html, encoding: encoding)

forçar UTF8 lá quebraria a análise de respostas não UTF8 de servidores web

Obrigado pelo esclarecimento. De volta à embalagem 3.4.0.