Problema extraño de codificación en la página de categorías

Estoy intentando rastrear un problema extraño en una instalación no dockerizada (me doy cuenta de que el soporte para este tipo de instalación es limitado/inexistente, así que solo busco algunas pistas sobre lo que podría estar mal aquí; nuestro equipo de empaquetado interno utilizó las instrucciones de ‘build de desarrollador’ para averiguar cómo construir los paquetes necesarios). He podido confirmar que el problema es específico de la forma en que hemos instalado; mi equipo de infraestructura no está dispuesto a usar una instalación dockerizada (prefieren construir todo ellos mismos), así que ejecuto instancias de sandbox que están dockerizadas y no dockerizadas con una copia de nuestra base de datos para verificar dónde está un problema, y esto es definitivamente un artefacto de la forma en que hemos instalado nuestra configuración.

Al actualizar de 3.3.2 a 3.3.3, algunos de nuestros empleados del foro no ingleses notaron que el texto “acerca de” para las secciones que usan caracteres acentuados no está codificado correctamente:

Curiosamente, podemos ver que el encabezado, así como todo el demás texto, está codificado correctamente. De hecho, el mensaje en sí que se usa para el mensaje “acerca de” está codificado correctamente:

Confirmé que es el mismo texto al editarlo y ver el cambio en la página de categorías.

Por lo tanto, es algo específico de la representación de ese texto en la página de categorías.

Al observar document.characterSet en mi navegador, se identifica correctamente como UTF-8. La base de datos también muestra el formato como UTF-8.

Me pregunto si alguien puede señalarme qué es diferente en la forma en que se representa este texto en la página de categorías. Mi suposición es que se trata de algún paquete de ruby que no está bien construido (tal vez falta soporte UTF-8) que se utiliza para representar ese texto, pero no otro texto en el sistema, o algo que procesa el texto del mensaje “acerca de” y lo trunca (lo que he notado que es el caso aquí; sin embargo, también tenemos un enlace a un foro francés externo que es un mensaje no truncado, pero supongo que todavía es evaluado por el mismo código).

Gracias por cualquier indicación. Estoy un poco perplejo aquí.

Veo que a veces es correcto:

Al extraer un archivo categories.json sin procesar, se muestra que solo está mal en el extracto:

        "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.).",

Al crear la misma categoría en try.discourse.org y comprobar categories.json, se obtiene el resultado correcto:

        "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.).",

No estoy seguro de cuál sería el siguiente paso para rastrear esto en tu instalación, pero tal vez centrarse en la ruta de código que genera el extracto ayude, además de saber que esto surgió de algo que interpreta la codificación UTF-8 como iso-8859-1.

Sí, esa es mi suposición: probablemente sea correcto buscar lo que genera el extracto. Solo que no estoy seguro de dónde está eso en el código. Pero saber que debo buscar el término “excerpt” es definitivamente útil. ¡Gracias!

Sí, me pareció que se estaba recibiendo como iso-8859-1 en algún momento, así que agradezco esa confirmación también (no estaba 100% seguro de que esa fuera la codificación incorrecta que estaba viendo, pero parecía correcta).

Lo que viste en try.discourse.org es lo que vi en mi instalación dockerizada también (bueno, el resultado final de que la codificación sea correcta :))

¡Gracias!

Puedes comprobarlo fácilmente con:

○ → 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.).'

Mi suposición es que algo está haciendo algún tipo de caché. Eso no ayuda mucho, pero es lo que intentaría buscar.

A menos que odien Docker, pueden crear sus propias imágenes con discourse_docker. Luego pueden ver exactamente lo que está sucediendo y no tener que confiar en las imágenes de nadie más.

Genial, gracias por eso.

Pensé que podría ser el caso, pero al cambiar el mensaje se produjo una actualización, así que no creo que sea caché, es algo que se codifica incorrectamente.

Sugerí algunas opciones, pero al final, el equipo de infraestructura optó por utilizar paquetes creados con el servicio de compilación. No creo que sea una cuestión de “odiamos Docker” (aunque podman probablemente sería más probable que quisieran usar), sino más bien una forma de usar las herramientas de gestión de configuración que están utilizando para gestionar todo de la misma manera. Tener algo único que use Docker/podman añadiría otras complejidades al uso de la configuración de CI/CD que están utilizando (o eso entiendo).

Así que, en última instancia, configuré mis dos entornos aislados para poder determinar dónde se originan los problemas y así poder informarlos al lugar adecuado; desafortunadamente, eso significa que cuando se trata de algo en cómo hemos construido las cosas, tengo que rastrear lo que estamos haciendo de manera diferente a una instalación estándar basada en Docker para poder solucionarlo.

Entiendo cómo piensan que la forma Discourse es una locura y realmente quieren que todo se gestione bajo un Sistema Unificado.

Pero. El último cliente que tuve que insistió en usar sus herramientas favoritas terminó pagándome cerca de 20 horas de trabajo para obtener una copia de seguridad utilizable y migrar a la configuración de alojamiento de discourse.org. El anterior pagó mucho más para ajustar su configuración personalizada y evitar que su sitio se cayera varias veces por semana, y un año después me pagó aún más para migrarlo a la configuración de alojamiento de discourse.org. :slight_smile:

¡Buena suerte!

3 Me gusta

Agradezco el consejo. La buena noticia es que las copias de seguridad de nuestro sistema de producción funcionan bien en una instalación dockerizada (lo he probado), así que si/cuando decidan que usar una instalación basada en Docker es el camino correcto, estaremos en buena forma. Tenemos muchos datos (migrados de vBulletin hace unos años a Discourse), y en general las cosas han funcionado bastante bien, con algunos inconvenientes extraños aquí y allá.

En consecuencia, hemos aprendido mucho sobre cómo funciona Discourse, así que no es algo malo en general. :slight_smile:

Parece que /categories.json es un punto final de la API en lugar de un archivo estático que se crea y luego se lee, por lo que creo que esto limita el problema a ruby o javascript. He encontrado dónde está el esquema para este punto final, pero al no estar particularmente familiarizado con ruby (he acumulado mucha experiencia en lenguajes de programación a lo largo de los años, por lo que leer la mayoría de los lenguajes no es un problema para mí, incluso si no puedo codificar en ellos; puedo captar la idea con bastante facilidad), pero parece que el javascript se ejecuta principalmente en el navegador, y el ruby se ejecuta en el servidor (aunque noto que nodejs también está instalado, por lo que esa generalización puede no ser del todo cierta).

Si puedo encontrar la función que procesa /categories (ya que parece que .json al final solo le dice al código cómo formatear la salida; veo un comportamiento similar en /top vs /top.rss, por ejemplo), entonces eso debería limitar dónde necesito buscar en el código, y eso me dirá qué gemas de ruby (estoy bastante seguro de que será código ruby) necesito verificar que estén correctamente compiladas.

2 Me gusta

Parece ser algo específico de las funciones de extracción; acabo de notar que esto también sucede en nuestras páginas de resultados de búsqueda:

(por ejemplo)

El texto es:

No veo un botón “Compartir”.

Que es algo que yo mismo cité en una respuesta a un usuario (panorain). Me topé con esto por error, ya que al intentar ver mi propia actividad, obtengo un error de servidor 500, y la salida de /logs muestra un error de tiempo de ejecución “la cadena de entrada no puede estar vacía” en lib/excerpt_parser.rb.

Parece que varias cosas se remontan a algo en el procesamiento de extractos, pero solo en las instalaciones de estilo de desarrollo.

En mi instalación basada en docker, puedo ver mi actividad sin errores; extrañamente, sin embargo, la base de datos en esa instalación se restaura a partir de una copia de seguridad reciente del servidor de producción, donde existe el problema.

2 Me gusta

Parece que actualizamos nokogiri a 1.17.2, y veo que la versión Dockerizada es 1.16.7. Sospecho que esa es la causa de este problema. Voy a ver cómo revertir esa actualización (y cualquier otra cosa que se haya actualizado al mismo tiempo).

Así que rebajé nuestro paquete para volver a usar nokogiri 1.16. Lo que no entiendo. Cada vez que actualizo una gema para reducir el empaquetado duplicado, compruebo si ha habido cambios relacionados en main, no ha habido ninguno. A menos que me haya perdido algo

        "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 puedes ver, tenemos el texto correcto dos veces, solo cuando pasa por PrettyText.excerpt se rompe. ¿Cómo se maneja eso en main?

@hendersj Ya estoy preparando un paquete main para que podamos probarlo con una copia de la base de datos.

Supongo que se manejó en DEV: Update nokogiri to 1.18.1 (#30554) · discourse/discourse@affe26f · GitHub

pero me pregunto… en lib/retrieve_title.rb

doc = Nokogiri.HTML5(html, encoding:)

¿no debería ser esto?

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

@pfaffman ese código extraño también está en la versión 3.4.0. :slight_smile:

¿podrías comprobar si realmente debería llamarse con una configuración de codificación vacía?

¿Alguna razón por la que creas eso? UTF-8 es el valor predeterminado.

[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

La función retrieve_title se utiliza para extraer títulos de URLs externas (por ejemplo, Youtube) y, aunque no estoy familiarizado íntimamente con esta ruta de código, me sorprendería que esta fuera la fuente de tu problema.

Si estás haciendo algo más (por ejemplo, usando esta función en un plugin personalizado), el parámetro de codificación allí proviene de la cabecera content-type del recurso recuperado:

        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)

por lo que se podría sospechar que el servidor web que responde está informando de un content-type incorrecto.

porque todas las demás llamadas en ese parche tienen encoding: parameters especificado.

solo la de retrieve title no lo tiene, lo que parece inconsistente. y no manejar correctamente la codificación UTF-8 fue toda la discusión que llevó a este hilo.

Ah:

es una abreviatura de:

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

forzar UTF8 allí rompería el análisis de respuestas que no son UTF8 de los servidores web.

Gracias por la aclaración. Volviendo al empaquetado 3.4.0.