Restauración fallando: posible problema con Data Explorer

Tengo un foro funcional y quiero recuperar algunas cosas que se dañaron hace unos días. Estoy en AWS, así que creé una imagen AMI del foro funcional, inicié una nueva instancia e intenté restaurar una copia de seguridad de hace unos días. La restauración falló con los mensajes que se muestran a continuación.

No puede tratarse de una incompatibilidad de versión o esquema, ya que el servidor se construyó a partir de una imagen fresca del foro funcional.

He intentado reconstruirlo.

He intentado restaurar desde una copia de seguridad diferente de hace solo un día, pero ocurre lo mismo.

Lo único extraño que he hecho es eliminar archivos PDF del directorio de cargas (…/uploads/original/1X/*.pdf) para liberar espacio. Lo intentaré de nuevo sin ese paso, pero parece poco probable que sea la causa.

> [2019-11-30 01:17:44] 'admin' ha iniciado la restauración.
> [2019-11-30 01:17:44] Marcando la restauración como en ejecución...
> [2019-11-30 01:17:44] Asegurando que existe /var/www/discourse/tmp/restores/default/2019-11-30-011744...
> [2019-11-30 01:17:44] Descargando el archivo comprimido al directorio temporal...
> [2019-11-30 01:23:24] Descomprimiendo el archivo comprimido, esto puede tardar un rato...
> [2019-11-30 01:27:52] No hay archivo de metadatos para extraer.
> [2019-11-30 01:27:52] Validando metadatos...
> [2019-11-30 01:27:52]   Versión actual: 20191129144706
> [2019-11-30 01:27:52]   Versión restaurada: 20191120015344
> [2019-11-30 01:27:52] Extrayendo el archivo de volcado...
> [2019-11-30 01:50:57] comando inválido \N
> [2019-11-30 01:50:57] comando inválido \N
> 
> 
> < se repite unas 100 veces >
> 
> [2019-11-30 01:51:07] comando inválido \N
> [2019-11-30 01:54:13] comando inválido \N
> [2019-11-30 01:54:13] EXCEPCIÓN: psql falló
> [2019-11-30 01:54:14] /var/www/discourse/lib/backup_restore/restorer.rb:331:in `restore_dump'
> /var/www/discourse/lib/backup_restore/restorer.rb:75:in `run'
> /var/www/discourse/lib/backup_restore.rb:166:in `block in start!'
> /var/www/discourse/lib/backup_restore.rb:163:in `fork'
> /var/www/discourse/lib/backup_restore.rb:163:in `start!'
> /var/www/discourse/lib/backup_restore.rb:22:in `restore!'
> /var/www/discourse/app/controllers/admin/backups_controller.rb:119:in `restore'
> etc...

Sin embargo, el error invalid command \N es típico de un desajuste de versión de Postgres…

root@example:/var/www/discourse# psql --version
psql (PostgreSQL) 10.10 (Debian 10.10-1.pgdg100+1)

Es la misma versión en el nuevo servidor y el foro funciona correctamente.

Suelo asociar esos errores con la coincidencia de versiones de PostgreSQL, pero vi esos errores \N el otro día en un sistema que se quedó sin espacio en disco (estaba restaurando en el mismo sistema que generó la copia de seguridad). No terminé de diagnosticar el problema (era otro problema extraño que tenía; restaurar una copia de seguridad en otro servidor solucionó el problema; me pregunté si restaurar en el mismo servidor lo habría solucionado).

Mencionaste que tenías poco espacio. Sospecho que ese es el problema. La restauración requiere mucho espacio, ya que descomprime la copia de seguridad y, por lo tanto, tiene dos copias completas de ella, además del espacio necesario para restaurarla y poder revertir en caso de fallo.

¿Así que tarda 23 minutos antes de fallar?

Las cosas empeoran, pero quizás nos acercamos más al problema real… Bajo la hipótesis de que necesitaba más espacio en disco, creé una nueva instancia a partir de mi imagen, esta vez con 100 GB, en lugar de los 50 GB anteriores. (Las copias de seguridad son de 5 GB cada una y se almacenan en S3.) Esta vez recibí un error explícito: “No space left on device” (Sin espacio disponible en el dispositivo). Sin embargo, df muestra un montón de espacio disponible.

> [2019-11-29 22:42:58] Asegurando que /var/www/discourse/tmp/restores/default/2019-11-29-224258 exista...
> [2019-11-29 22:42:58] Descargando el archivo comprimido al directorio tmp...
> [2019-11-29 22:45:46] Descomprimiendo el archivo comprimido, esto puede tardar un rato...
> [2019-11-29 22:51:46] No hay archivo de metadatos para extraer.
> [2019-11-29 22:51:46] Validando metadatos...
> [2019-11-29 22:51:46]   Versión actual: 20191129144706
> [2019-11-29 22:51:46]   Versión restaurada: 20191108000414
> [2019-11-29 22:51:46] Extrayendo el archivo volcado...
> [2019-11-29 22:53:47] EXCEPCIÓN: No space left on device @ io_write - /shared/tmp/restores/default/2019-11-29-224258/dump.sql

> [ec2-user@ip-172-31-47-237 discourse]$ df / -h
> Filesystem      Size  Used Avail Use% Mounted on
> /dev/xvda1       99G   28G   71G  28% /

Curiosamente, no hay nada en el directorio mencionado:

> /var/www/discourse# ls /shared/tmp/*
> /shared/tmp/backups:
> < vacío >
> /shared/tmp/restores:
> < vacío >

¿Podría ser un problema con el archivo de intercambio (swap)? La instancia EC2 es una t2.small con 2 GB de memoria, por lo que hace mucho tiempo creé un archivo de intercambio en el foro funcional. Asumo que el archivo de intercambio se replicaría en la nueva instancia. No soy un experto, pero creo que el archivo de intercambio existe, porque no me permitió crear uno nuevo, y porque:

> /var/www/discourse# swapon -s
> Filename                                Type            Size    Used    Priority
> /swapfile                               file            2097148 1024    -2

Podría ser un problema de inodos. ¿Qué te indica df -i?

df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
devtmpfs 252562 437 252125 1% /dev
tmpfs 255203 1 255202 1% /dev/shm
/dev/xvda1 6553600 737194 5816406 12% /

Saliendo de mi nivel, pero supongo que eso no está mal, ¿verdad?

No, está bien.

‘No hay espacio restante en el dispositivo’ no solo ocurre cuando un dispositivo se queda sin gigabytes, sino también cuando el sistema de archivos se queda sin inodos. Pero claramente ese no es el problema aquí. (iUse% estaría al 100% en ese caso).

Aún no hay resultados. Pensé que probaría restaurar en una nueva instancia de Lightsail, en lugar de lanzar una AMI de mi instancia EC2 funcional. Aún falla, pero los mensajes son algo diferentes.

Tanto la instancia antigua como la nueva están actualizadas, ambas son instalaciones estándar de Docker y ambas ejecutan la misma versión de PostgreSQL:

psql --version
psql (PostgreSQL) 10.10 (Debian 10.10-1.pgdg100+1)

¿Es esto normal:

Creando funciones faltantes en el esquema discourse_functions
No se puede restaurar en un esquema diferente, restaurando en el lugar

¿Podría estar relacionado con los plugins? Tengo varios plugins, tanto oficiales como personalizados, instalados en el sitio de “origen”. Algunos utilizan campos de usuario personalizados. He intentado restaurar en sitios de “destino” limpios, tanto con como sin plugins.

¿Alguna sugerencia sobre cómo empezar a comparar los esquemas?

> [2019-12-07 04:51:36] 'admin' ha iniciado la restauración.
> [2019-12-07 04:51:36] Marcando la restauración como en curso...
> [2019-12-07 04:51:36] Asegurando que /var/www/discourse/tmp/restores/default/2019-12-07-045136 exista...
> [2019-12-07 04:51:36] Descargando el archivo comprimido al directorio temporal...
> [2019-12-07 04:53:49] Descomprimiendo el archivo, esto puede tardar un rato...
> [2019-12-07 04:57:12] No hay archivo de metadatos para extraer.
> [2019-12-07 04:57:12] Validando metadatos...
> [2019-12-07 04:57:12]   Versión actual: 20191129144706
> [2019-12-07 04:57:12]   Versión restaurada: 20191120015344
> [2019-12-07 04:57:12] Extrayendo el archivo volcado...
> [2019-12-07 04:59:10] Creando funciones faltantes en el esquema discourse_functions
> [2019-12-07 04:59:11] No se puede restaurar en un esquema diferente, restaurando en el lugar
> [2019-12-07 05:05:02] ERROR: la transacción actual está abortada, los comandos se ignorarán hasta el final del bloque de transacción
> [2019-12-07 05:05:03] ERROR: la transacción actual está abortada, los comandos se ignorarán hasta el final del bloque de transacción
> < se repite unas 100 veces >
> [2019-12-07 05:05:03] ERROR: la transacción actual está abortada, los comandos se ignorarán hasta el final del bloque de transacción
> [2019-12-07 05:05:03] EXCEPCIÓN: psql falló
> [2019-12-07 05:05:03] /var/www/discourse/lib/backup_restore/restorer.rb:331:in `restore_dump'
> /var/www/discourse/lib/backup_restore/restorer.rb:75:in `run'
> /var/www/discourse/lib/backup_restore.rb:166:in `block in start!'
> /var/www/discourse/lib/backup_restore.rb:163:in `fork'
> /var/www/discourse/lib/backup_restore.rb:163:in `start!'
> /var/www/discourse/lib/backup_restore.rb:22:in `restore!'
> /var/www/discourse/app/controllers/admin/backups_controller.rb:119:in `restore'
> < resto del seguimiento de pila >

Sí, eso es normal.

Claramente hay algo fallando dentro de PostgreSQL. ¿Has revisado sus registros?

Una posibilidad remota: ¿podría estar relacionado con la memoria? ¿Puedes intentar monitorear la salida de free -m durante la restauración y ver si se agota la memoria (virtual)?

Sé que es una pregunta difícil ya que no sabemos cuál es el problema, pero, en general, ¿debería instalar los complementos en el sitio de destino antes de intentar la restauración? ¿O la restauración descarga y genera los complementos?

Sí, deberías; la restauración no lo hará por ti.

Por otro lado, no creo que ese sea tu problema, ya que la restauración sí se encargará de la estructura correcta de la base de datos (incluyendo lo específico de los complementos).

He creado una nueva instancia Lightsail de 20 $ con 4 GB de memoria. Observé ‘free -m’ durante la restauración. Siempre había suficiente memoria libre y disponible.

Los errores específicos varían dependiendo de si tengo los plugins instalados, pero probablemente se deban a la misma causa raíz. En este caso, no instalé los plugins antes de la copia de seguridad. Hay dos clases de errores:

  1. En los registros de postgres, obtengo muchos de estos, a veces en el carácter 34 y a veces en el carácter 41.

discourse@discourse ERROR: la relación “user_auth_tokens” no existe en el carácter 34

Estos no tienen una salida correspondiente en la página de registro de administración > copia de seguridad >, y la restauración continúa durante varios minutos a pesar de estos errores.

  1. El segundo error depende de si los plugins están instalados. En este caso, no los instalé, por lo que obtengo un error sobre Data Explorer, y es cuando todo se complica.

En administración > copia de seguridad > registros:

[2019-12-07 07:38:34] CREATE INDEX
[2019-12-07 07:38:34] CREATE INDEX
[2019-12-07 07:38:34] ERROR: no se pudo crear el índice único “index_plugin_store_rows_on_plugin_name_and_key”
[2019-12-07 07:38:34] DETALLE: La clave (plugin_name, key)=(discourse-data-explorer, q:-6) está duplicada.
[2019-12-07 07:38:34] ERROR: la transacción actual está abortada, los comandos se ignoran hasta el final del bloque de transacción
[2019-12-07 07:38:34] ERROR: la transacción actual está abortada, los comandos se ignoran hasta el final del bloque de transacción
< se repite 1000 veces y finaliza >

Y la salida correspondiente en el registro de postgres:

2019-12-07 07:38:34.718 UTC [8991] discourse@discourse LOG: duración: 165.427 ms declaración: CREATE INDEX index_notifications_on_user_
id_and_topic_id_and_post_number ON public.notifications USING btree (user_id, topic_id, post_number);
2019-12-07 07:38:34.767 UTC [8991] discourse@discourse ERROR: no se pudo crear el índice único “index_plugin_store_rows_on_plugin_name_an
d_key”
2019-12-07 07:38:34.767 UTC [8991] discourse@discourse DETALLE: La clave (plugin_name, key)=(discourse-data-explorer, q:-6) está duplicada.
2019-12-07 07:38:34.767 UTC [8991] discourse@discourse DECLARACIÓN: CREATE UNIQUE INDEX index_plugin_store_rows_on_plugin_name_and_key O
N public.plugin_store_rows USING btree (plugin_name, key);
2019-12-07 07:38:34.984 UTC [8991] discourse@discourse ERROR: la transacción actual está abortada, los comandos se ignoran hasta el final del bloque de transacción
2019-12-07 07:38:34.984 UTC [8991] discourse@discourse DECLARACIÓN: CREATE INDEX index_policy_users_on_post_policy_id_and_user_id ON pub
lic.policy_users USING btree (post_policy_id, user_id);

Creo que el #1 no es fatal y es solo un efecto secundario de la restauración en el lugar.

Podrías considerar (exportar y) eliminar todas las consultas del explorador de datos y desinstalar el plugin del explorador de datos antes de crear tu copia de seguridad.

Alternativamente, ¿puedes publicar el contenido relevante de la tabla plugin_store_rows?

En efecto, hay consultas duplicadas con pares duplicados (plugin_name, key), por ejemplo, q:-11 y q:-2, pero con IDs únicos. No veo ningún patrón entre las duplicadas; no son mis consultas favoritas ni nada por el estilo.

Así que mi próximo paso será eliminar las duplicadas, hacer una copia de seguridad e intentar restaurar desde ella.

SELECT id, plugin_name, key from plugin_store_rows
WHERE plugin_name = ‘discourse-data-explorer’
ORDER BY key

id plugin_name key
1138 discourse-data-explorer q:-1
1136 discourse-data-explorer q:-10
813 discourse-data-explorer q:10
1142 discourse-data-explorer q:-11
1397 discourse-data-explorer q:-11
825 discourse-data-explorer q:11
889 discourse-data-explorer q:13
1004 discourse-data-explorer q:14
1005 discourse-data-explorer q:15
1043 discourse-data-explorer q:17
1044 discourse-data-explorer q:18
514 discourse-data-explorer q:-2
1249 discourse-data-explorer q:-2
764 discourse-data-explorer q:2
1053 discourse-data-explorer q:21
1066 discourse-data-explorer q:22
1082 discourse-data-explorer q:23
1097 discourse-data-explorer q:24
1131 discourse-data-explorer q:26
1132 discourse-data-explorer q:27
1134 discourse-data-explorer q:28
1135 discourse-data-explorer q:29
775 discourse-data-explorer q:3
1137 discourse-data-explorer q:30
1140 discourse-data-explorer q:31
1141 discourse-data-explorer q:32
1143 discourse-data-explorer q:33
1149 discourse-data-explorer q:34
1155 discourse-data-explorer q:35
1156 discourse-data-explorer q:36
1157 discourse-data-explorer q:37
1158 discourse-data-explorer q:38
1161 discourse-data-explorer q:39
513 discourse-data-explorer q:-4
777 discourse-data-explorer q:4
1211 discourse-data-explorer q:40
1215 discourse-data-explorer q:41
1223 discourse-data-explorer q:42
1224 discourse-data-explorer q:43
1225 discourse-data-explorer q:44
1226 discourse-data-explorer q:45
1269 discourse-data-explorer q:46
1272 discourse-data-explorer q:47
1273 discourse-data-explorer q:48
1274 discourse-data-explorer q:49
1279 discourse-data-explorer q:50
1281 discourse-data-explorer q:51
1282 discourse-data-explorer q:52
1301 discourse-data-explorer q:53
1349 discourse-data-explorer q:54
1369 discourse-data-explorer q:55
1373 discourse-data-explorer q:56
1384 discourse-data-explorer q:57
1387 discourse-data-explorer q:58
1396 discourse-data-explorer q:59
1222 discourse-data-explorer q:-6
1348 discourse-data-explorer q:-6
781 discourse-data-explorer q:6
763 discourse-data-explorer q:-7
782 discourse-data-explorer q:7
515 discourse-data-explorer q:-8
791 discourse-data-explorer q:8
1139 discourse-data-explorer q:-9
798 discourse-data-explorer q:9
507 discourse-data-explorer q:_id

En realidad, ¿cómo puedo eliminar los duplicados? Los tres son propiedad de “system”, por lo que no puedo editarlos ni eliminarlos.

Encontré el patrón. Cuando ejecuto una consulta propiedad del sistema, se crea un duplicado, lo que evidentemente rompe la restauración.

No puedo reproducirlo en un sitio de prueba limpio, pero ocurre de forma consistente en mi sitio de producción. He instalado todos los complementos de producción en el sitio de prueba, pero aún así no logro reproducirlo.

  1. ¿Cómo puedo averiguar qué está mal en mi sitio de producción?

  2. ¿Cómo puedo eliminar las consultas duplicadas, ya que son propiedad del sistema? ¿Tengo que usar sudo -u postgres psql discourse...? Suena aterrador.

Para que la copia de seguridad sea restaurable, probablemente puedas eliminar las líneas duplicadas del archivo SQL de respaldo.

¿Es posible que, por alguna razón, la base de datos de desarrollo carezca de ese índice?