Hoy intenté actualizar mi instalación de Discourse, de 2.9.0.beta9 a 2.9.0.beta10. Pero esto salió terriblemente mal.
Mi primer intento fue en la consola
cd /var/discourse
sudo git pull
sudo ./launcher rebuild app
Esto finalmente se interrumpió, con el siguiente mensaje en algún lugar en medio de todos los registros
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'
Pude restaurar el foro simplemente iniciando la imagen antigua (que todavía andaba por ahí).
Mi segundo intento fue a través de la consola web (/admin/upgrade). Pero esto finalmente se atascó con el mismo tipo de error: ERROR: cannot extract elements from a scalar. Al volver al panel de actualización, me dijo que todos los componentes se habían actualizado correctamente. Pero después de reiniciar el contenedor, el servidor ahora arrojó un error HTTP 500
Hice una instalación limpia en una máquina separada, empezando desde cero. Pude instalar la versión beta10, pero intentar restaurar desde una copia de seguridad causó exactamente el mismo error.
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
Si entiendo correctamente, esto sucede porque la función de psql jsonb_array_elements espera una matriz, pero recibe un valor NULL.
El sitio que vi tenía un montón de esos Post Custom Fields que contenían un montón de cadenas que habían sido codificadas varias veces para que fueran inutilizables.
Algunas soluciones, en orden de complejidad:
dejar de usar el plugin (y quizás cambiar a los nuevos votos positivos)
eliminarlos todos
eliminar los malos
editar esos campos para que los datos vuelvan a ser cadenas json válidas.
El sitio que vi tenía datos malos de años de antigüedad. Esto parece ser un error que existió hace años y que ahora se está descubriendo.
Podría ser posible escribir código para arreglar las cadenas json rotas, pero no pude ver cómo hacerlo en 10 minutos.
Esto se siente como contaminación de datos de las antiguas publicaciones de preguntas y respuestas, como dijiste. También me parece gracioso que esto aparezca ahora, dado que esa migración es de noviembre de 2021. ¿Supongo que es porque JayJay solo está cambiando al nuevo plugin ahora? En cualquier caso, volveremos a examinar esa migración.
Si eliminara el plugin, restaurar la copia de seguridad aún ejecutaría la consulta que causa el problema.
Por lo tanto, necesitaría eliminar tanto el plugin como cualquier tabla + consulta que sea utilizada por este plugin.
¿Cómo sabría las tablas involucradas?
Voy a intentarlo ahora. He revisado el archivo dump.sql, que forma parte de la copia de seguridad, y no hay ninguna mención de ninguna tabla question_answer_* en absoluto. Así que esto me da esperanzas…
Los datos corruptos se encuentran en la tabla PostCustmField. Pero si no tienes el plugin, no intentará migrar esos datos a la nueva tabla.
El problema no es la migración en sí, sino que en algún momento del pasado los datos se corrompieron. Se ha roto, pero se rompió de una manera que pasó desapercibida durante años. Creo que quizás cada nuevo voto positivo la corrompió aún más. Una solución razonable podría ser ignorar los datos corruptos, quizás marcándolos como corruptos (quizás renombrando el campo personalizado) para que las futuras migraciones puedan ignorarlos y alguien pueda arreglarlos manualmente si así lo desea.
Éxito en producción también. Desactivé el plugin y seguimos adelante.
Todavía me pregunto por qué no me encontré con este problema antes. Como dije, cada mes, como rutina, actualizamos todos nuestros sistemas, así que debería haber visto este problema antes.
¿Cómo podría conectarme yo mismo a la base de datos de Discourse para verificar qué hay en la tabla post_custom_fields?
Respetuosamente no estoy de acuerdo contigo en eso
El antiguo plugin QnA utilizaba el tipo de datos de campo personalizado estándar de Discourse, que está diseñado para manejar la conversión de ese campo a JSON. Pero eso simplemente no funciona en algunos casos (como este), por lo que (idealmente) necesitas incluir verificaciones de formato cuando trabajas con campos personalizados de Discourse (particularmente si estás trabajando con código y datos bastante antiguos como este). Sugeriría que eso es lo que el plugin Upvotes necesita hacer aquí, que de hecho es de donde proviene el error inmediato.
Puedes usar el plugin Data Explorer para verificar lo que tienes allí.
Bueno, en su mayor parte te cedo la razón en esto, y creo que estamos de acuerdo. Creo que es cierto que la migración funciona si los datos no están rotos. Estamos de acuerdo en que si los datos están rotos, debería fallar de manera más elegante de lo que lo hace ahora.
Sí, pero es probable que los datos se corrompieran hace años (ese es el caso en el sitio que conozco), pero no te diste cuenta porque no falló catastróficamente. Estoy bastante seguro de que no estaba gestionando los votos positivos como se esperaba, pero nadie se dio cuenta.
Lo haría desde rails, algo como esto:
./launcher enter app
rails c
Luego, cosas como esta:
all_votes=PostCustomField.where(name: "vote_history")
likely_broken_votes=PostCustomField.where(name: "vote_history").where("value like '\\\"%'")
Mira solo el id y los datos:
all_votes.pluck(:id,:value)
Obtén solo un pcf:
pcf=PostCustomField.find(1234)
Arréglalo
pcf.value='the stuff you really want in it'
pcf.save
Algunas personas tienen una versión muy antigua del plugin de preguntas y respuestas con mi nombre de usuario personal de GitHub en la URL del repositorio en su archivo app.yml.
Hace años transferí el plugin QnA a paviliondev. Github redirige las URL de los repositorios cuando se transfieren, por lo que las URL antiguas con mi nombre de usuario personal siguieron funcionando.
Años después, Pavilion transfirió el Plugin de Preguntas y Respuestas a Discourse. Discourse inicialmente mantuvo el nombre discourse-question-answer.
Hace unos meses creé mi propia bifurcación (fork) del plugin alojado en discourse, mientras todavía se llamaba discourse-question-answer.
Las personas con enlaces muy antiguos al plugin QnA con mi cuenta personal de GitHub ahora estaban clonando mi nueva bifurcación del discourse-question-answer significativamente actualizado alojado en discourse. En otras palabras, un enlace muy antiguo ahora apuntaba a una bifurcación de un nuevo plugin. A pesar de los años transcurridos, debería haberlo previsto, así que disculpas por ello. He eliminado esa bifurcación.
Discourse cambió el nombre de discourse-question-answer a discourse-upvotes. Este cambio de nombre no ha tenido un impacto material en su caso @Jaap-Jan_Swijnenburg, pero es la razón por la que ahora está clonando (inesperadamente) una bifurcación de ese repositorio.
Una migración en discourse/discourse-upvotes (anteriormente discourse-question-answer) asume que la columna value en post_custom_fields es segura para JSON.
Los datos antiguos creados por el plugin cuando era angusmcleod/discourse-question-answer (hace años) no se guardaron como JSON válido por la preocupación HasCustomFields en discourse/discourse. Supongo, pero es probable que estos datos se agregaran antes de que se agregara la verificación de tipo JSON hace 4 años (todavía es posible terminar con JSON no válido en campos personalizados registrados como JSON en casos extremos).
Por lo tanto:
Cuando las personas con la URL (obsoleta desde hace años) angusmcleod/discourse-question-answer en su app.yml actualizan su Discourse, se clona la migración en la nueva versión del plugin, se ejecuta la migración y potencialmente se crea este error.
Hay algunas soluciones para esto:
@Jaap-Jan_Swijnenburg en su caso, solo necesita eliminar la referencia a mi antiguo plugin QnA y podrá reconstruir su sitio. Eso es todo; nada más. Parece que eso es lo que ha hecho
La migración del plugin discourse/discourse-upvotes (anteriormente discourse-question-answer) podría actualizarse para manejar valores no JSON en la columna value en post_custom_fields.
Observaría que la opción 2 también manejaría el caso adicional de personas que realmente desean cambiar de una versión antigua del plugin QnA a discourse-upvotes. En ese caso, la migración se ejecutará y fallará si alguna entrada en la columna value de post_custom_fields no es JSON válido.