Restauración falla: no se pudo crear el índice único

Hola, la restauración de la copia de seguridad falla con el siguiente error:

CREATE INDEX
ERROR: no se pudo crear el índice único "index_incoming_referers_on_path_and_incoming_domain_id"
DETALLE: La clave (path, incoming_domain_id)=(/m/search, 25) está duplicada.
EXCEPCIÓN: psql falló: DETALLE: La clave (path, incoming_domain_id)=(/m/search, 25) está duplicada.

Encontré un tema similar pero no pude entender qué hay que hacer: https://meta.discourse.org/t/getting-this-error-during-restore-could-not-create-unique-index/

Agradezco cualquier ayuda paso a paso para principiantes para solucionarlo.

2 Me gusta

Este problema con la tabla incoming_referers ha surgido varias veces recientemente. No estoy seguro de por qué esa tabla en particular está causando problemas, pero parece probable que los problemas estén relacionados. Quizás alguien más del equipo de Discourse tenga ideas sobre qué podría estar causando la creación de registros duplicados.

¿Todavía tienes acceso al sitio donde creaste el archivo de respaldo? Si es así, la solución es eliminar el registro duplicado de la base de datos y luego crear un nuevo archivo de respaldo. Para hacerlo, deberías conectarte vía SSH al servidor antiguo y cd al directorio /var/discourse:

cd /var/discourse

Luego ejecuta

./launcher enter app

A continuación, ingresa a la consola de Rails con

rails c

Deberías ver entonces un indicador similar a este:

[1] pry(main)>

Intenta ejecutar el siguiente comando desde la consola de Rails y avísanos qué devuelve:

IncomingReferer.where(path: "/m/search")

Debería devolver un array con dos o más registros.

2 Me gusta

Gracias.
Lo ejecutaré por la mañana y te informaré.

1 me gusta

Esto es de la instalación anterior: parece que solo hay un registro.

[1] pry(main)> IncomingReferer.where(path: "/m/search")
=> [#<IncomingReferer:0x00005638d834b130
  id: 5153,
  path: "/m/search",
  incoming_domain_id: 25>]
[2] pry(main)>

Edición: también lo probé en el nuevo servidor. Muestra:

[1] pry(main)> IncomingReferer.where(path: "/m/search")
=> []
[2] pry(main)>

¡Gracias por verificarlo! El resultado que obtuviste es exactamente el mismo que yo vi en otro sitio más temprano hoy. Es un problema que se puede resolver, pero voy a intentar que uno de nuestros ingenieros revise qué está sucediendo.

2 Me gusta

Mi principal motivo para migrar los servidores era que estaba en Debian 8, que dejará de recibir soporte.
Ante este problema de restauración, opté por actualizar a Debian 9 en el mismo servidor. Ha sido un éxito, así que por ahora puedo respirar aliviado.
Gracias por su apoyo.

Reemplaza esta línea

Necesitas realizar una búsqueda difusa para que no asuma que el índice funciona. Creo que un solo signo de porcentaje es suficiente si está al principio.

Simplemente puedes eliminar el registro extra. Para hacerlo correctamente, sin embargo, necesitas actualizar la otra tabla que se vincula a esta. Tengo que consultarlo cada vez, ya que hay varias tablas diferentes que se relacionan con esto.

Este problema se atribuye a extensiones de terceros, lo cual no tiene mucho sentido. Parece que debe ser culpa de PostgreSQL, pero no lo sé. Me encuentro con esto un par de veces al mes, parece (puntuación) en varios sitios.

5 Me gusta

También tengo un problema de clave duplicada, ¿hay una solución documentada?

discourse=# REINDEX SCHEMA CONCURRENTLY public;
    ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_ccnew"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 1905) is duplicated.

[1] pry(main)> IncomingReferer.where(path: "/m/search")
=> [#<IncomingReferer:0x0000557176d3f210 id: 44231, path: "/m/search", incoming_domain_id: 4>,
 #<IncomingReferer:0x0000557176d925c8 id: 42228, path: "/m/search", incoming_domain_id: 26>]

Aunque acabo de actualizar mi servidor en el mismo lugar y, por lo tanto, ya no restauraré a un nuevo servidor, lo probé por curiosidad y no encontré ningún registro con búsqueda difusa:

[1] pry(main)> IncomingReferer.where(path: "%/m/search%")
=> []
[2] pry(main)> IncomingReferer.where(path: "%/m/search")
=> []
[3] pry(main)> IncomingReferer.where(path: "/m/search%")
=> []

Necesitas usar LIKE para que los comodines funcionen:

IncomingReferer.where("path LIKE '%/m/search%'")
4 Me gusta

Eso trajo bastante más claves duplicadas.

[1] pry(main)> IncomingReferer.where("path LIKE '%/m/search%'")
=> [#<IncomingReferer:0x0000557eaa7ed488 id: 408, path: "/m/search", incoming_domain_id: 26>,
 #<IncomingReferer:0x0000557eaabd80c0 id: 1508, path: "/m/search", incoming_domain_id: 45>,
 #<IncomingReferer:0x0000557eaabe3268 id: 2216, path: "/m/search", incoming_domain_id: 420>,
 #<IncomingReferer:0x0000557eaabe2f20 id: 3081, path: "/m/search", incoming_domain_id: 230>,
 #<IncomingReferer:0x0000557eaabe2c00 id: 33210, path: "/m/search", incoming_domain_id: 4>,
 #<IncomingReferer:0x0000557eaabe2908 id: 44231, path: "/m/search", incoming_domain_id: 4>,
 #<IncomingReferer:0x0000557eaabe27c8 id: 42228, path: "/m/search", incoming_domain_id: 26>]
2 Me gusta

Simplemente eliminaría todas las filas duplicadas… hay poco valor en esta información.

1 me gusta

Encantado de hacerlo. ¿Puedes proporcionarme el comando correcto? No estoy muy familiarizado con PostgreSQL en particular, pero sí conozco SQL.

Me alegra escuchar eso. He estado actualizando laboriosamente la otra tabla que enlaza con estas. Es un gran dolor de cabeza porque nunca recuerdo cómo se llamaba, así que es la primera vez una y otra vez.

IncomingReferer.find(44231).destroy
IncomingReferer.find(42228).destroy
2 Me gusta

Eliminar esas dos duplicaciones tuvo éxito, pero al reconstruir los índices posteriormente surgieron nuevos errores. ¿Es esto un problema grave? ¿Cómo podemos arreglarlo o eliminar esa fila de búsqueda 3433?

[1] pry(main)> IncomingReferer.find(44231).destroy
=> #<IncomingReferer:0x000055734c65d8e8 id: 44231, path: "/m/search", incoming_domain_id: 4>
[2] pry(main)> IncomingReferer.find(42228).destroy
=> #<IncomingReferer:0x000055734cd81a70 id: 42228, path: "/m/search", incoming_domain_id: 26>
postgres=# \connect discourse
Ahora estás conectado a la base de datos "discourse" como usuario "postgres".
discourse=# REINDEX SCHEMA CONCURRENTLY public;
WARNING:  no se puede reconstruir concurrentemente el índice inválido "public.incoming_referers_pkey_ccnew", omitiendo
WARNING:  no se puede reconstruir concurrentemente el índice inválido "public.index_incoming_referers_on_path_and_incoming_domain_id_ccnew", omitiendo
WARNING:  no se puede reconstruir concurrentemente el índice inválido "pg_toast.pg_toast_2782645_index_ccnew", omitiendo
ERROR:  no se pudo crear el índice único "index_incoming_referers_on_path_and_incoming_domain_id_ccnew1"
DETAIL:  La clave (path, incoming_domain_id)=(/search/, 3433) está duplicada.
CONTEXT:  worker paralelo
1 me gusta

Aquí está el código que maneja la creación… esto debería estar manejándolo correctamente, pero ¿podríamos actualizarlo a una inserción ON CONFLICT si es necesario?

4 Me gusta

Intenté reconstruir manualmente esos 4 índices. Dos tuvieron éxito y dos fallaron. ¿Debería eliminar esas dos filas duplicadas?

discourse=# REINDEX INDEX CONCURRENTLY "public"."incoming_referers_pkey_ccnew";
REINDEX
discourse=# REINDEX INDEX CONCURRENTLY "public"."index_incoming_referers_on_path_and_incoming_domain_id_ccnew";
ERROR:  no se pudo crear el índice único "index_incoming_referers_on_path_and_incoming_domain_id_cc_ccnew"
DETAIL:  La clave (path, incoming_domain_id)=(/search/, 1861) está duplicada.
discourse=# REINDEX INDEX CONCURRENTLY "pg_toast"."pg_toast_2782645_index_ccnew";
REINDEX
discourse=# REINDEX INDEX CONCURRENTLY "index_incoming_referers_on_path_and_incoming_domain_id_ccnew1";
ERROR:  no se pudo crear el índice único "index_incoming_referers_on_path_and_incoming_domain_id_c_ccnew1"
DETAIL:  La clave (path, incoming_domain_id)=(/search/, 1905) está duplicada.
1 me gusta

Sí, por favor elimina las filas duplicadas.

@riking La corrupción de índices en PostgreSQL es un error de PostgreSQL, no de Discourse. Sin duda podemos mejorar el rendimiento de esa inserción, pero el error de PostgreSQL debe solucionarse en PostgreSQL.

Mi suposición es que tiene algo que ver con algún tipo de apagado brusco del motor de la base de datos, quizás por una pérdida de energía.

2 Me gusta

Esa es una explicación razonable. ¿./launcher shutdown app (o rebuild) realiza un apagado limpio de PostgreSQL de alguna manera? Ah, pero apuesto a que una actualización no supervisada no sabe cómo realizar un apagado limpio de los contenedores de Docker, ¿verdad?