Échec de la restauration : impossible de créer l'index unique

Bonjour, la restauration de la sauvegarde échoue avec l’erreur suivante :

CREATE INDEX
ERREUR : impossible de créer l'index unique "index_incoming_referers_on_path_and_incoming_domain_id"
DÉTAIL : La clé (path, incoming_domain_id)=(/m/search, 25) est dupliquée.
EXCEPTION : psql a échoué : DÉTAIL : La clé (path, incoming_domain_id)=(/m/search, 25) est dupliquée.

J’ai trouvé un sujet similaire mais je n’ai pas pu comprendre ce qu’il faut faire : https://meta.discourse.org/t/getting-this-error-during-restore-could-not-create-unique-index/

Je vous remercie par avance pour toute aide étape par étape, même pour les débutants, afin de résoudre ce problème.

Ce problème concernant la table incoming_referers est survenu à plusieurs reprises récemment. Je ne sais pas pourquoi cette table en particulier pose problème, mais il semble probable que les problèmes soient liés. Peut-être qu’un autre membre de l’équipe Discourse aura des idées sur ce qui pourrait causer la création de doublons.

Avez-vous toujours accès au site sur lequel vous avez créé le fichier de sauvegarde ? Si oui, la solution consiste à supprimer l’enregistrement en double de la base de données, puis à créer un nouveau fichier de sauvegarde. Pour ce faire, connectez-vous en SSH à l’ancien serveur et naviguez vers le répertoire /var/discourse :

 cd /var/discourse

Ensuite, exécutez :

./launcher enter app

Puis, lancez la console Rails avec :

rails c

Vous devriez alors voir une invite ressemblant à ceci :

[1] pry(main)>

Essayez d’exécuter la commande suivante depuis la console Rails et indiquez-nous ce qu’elle renvoie :

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

Elle devrait retourner un tableau contenant deux enregistrements ou plus.

Merci.
Je lancerai cela demain matin et vous tiendrai informé.

Cela provient de l’ancienne installation — on dirait qu’il n’y a qu’un seul enregistrement ?

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

Édition : essayé également sur le nouveau serveur. Il affiche :

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

Merci d’avoir vérifié cela ! Le résultat que vous avez obtenu est en fait le même que celui que j’ai observé sur un autre site plus tôt aujourd’hui. C’est un problème soluble, mais je vais essayer de demander à l’un de nos ingénieurs d’examiner ce qui se passe.

Mon objectif principal pour déplacer les serveurs était que j’étais sous Debian 8, qui va bientôt cesser d’être prise en charge.
Face à ce problème de restauration, j’ai opté pour la mise à niveau vers Debian 9 sur le même serveur. Cela a fonctionné, ce qui offre un peu de répit pour l’instant.
Merci pour votre soutien.

Remplacez cette ligne

Vous devez effectuer une recherche floue afin qu’elle ne suppose pas que l’index fonctionne. Je pense qu’un seul signe pourcentage suffit s’il se trouve au début.

Vous pouvez simplement supprimer l’enregistrement supplémentaire. Pour le faire correctement, vous devez mettre à jour l’autre table qui fait référence à celle-ci. Je dois chercher cela à chaque fois car il y a plusieurs tables différentes impliquées.

Ce problème est attribué à des extensions tierces, ce qui n’a pas beaucoup de sens. Il semble que cela doive être la faute de PostgreSQL, mais je ne sais pas. Je rencontre ce problème plusieurs fois par mois (score) sur plusieurs sites.

Je rencontre également un problème de clé en double. Existe-t-il une solution documentée ?

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>]

Même si je viens de mettre à jour mon serveur sur place et que je ne restaurerai donc plus sur un nouveau serveur, j’ai essayé cela par curiosité et je n’ai trouvé aucun enregistrement avec une recherche floue :

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

Vous devez utiliser LIKE pour que les caractères génériques fonctionnent :

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

Cela a fait apparaître pas mal de clés en double.

[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>]

Je supprimerais carrément toutes les lignes en double… cette information a peu de valeur.

Avec plaisir, pouvez-vous fournir la commande correcte ? Je ne connais pas spécifiquement PostgreSQL, mais je maîtrise le SQL.

C’est bon à savoir. J’ai été obligé de mettre à jour manuellement l’autre table qui fait référence à celles-ci. C’est un vrai casse-tête, car je n’arrive jamais à me souvenir de ce que c’était, donc c’est la première fois que je refais tout, encore et encore.

IncomingReferer.find(44231).destroy
IncomingReferer.find(42228).destroy

La suppression de ces deux doublons a réussi, mais la reconstruction ultérieure des index a généré de nouvelles erreurs. S’agit-il d’un problème majeur ? Comment pouvons-nous corriger cela ou supprimer la ligne de recherche 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
Vous êtes maintenant connecté à la base de données "discourse" en tant qu'utilisateur "postgres".
discourse=# REINDEX SCHEMA CONCURRENTLY public;
WARNING:  cannot reindex invalid index "public.incoming_referers_pkey_ccnew" concurrently, skipping
WARNING:  cannot reindex invalid index "public.index_incoming_referers_on_path_and_incoming_domain_id_ccnew" concurrently, skipping
WARNING:  cannot reindex invalid index "pg_toast.pg_toast_2782645_index_ccnew" concurrently, skipping
ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_ccnew1"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 3433) is duplicated.
CONTEXT:  parallel worker

Voici le code gérant la création… cela devrait le gérer correctement, mais nous pourrions le mettre à jour vers une insertion ON CONFLICT si nécessaire ?

J’ai essayé de reconstruire manuellement ces 4 index. Deux ont réussi, deux ont échoué. Dois-je supprimer ces deux lignes en double ?

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:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_cc_ccnew"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 1861) is duplicated.
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:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_c_ccnew1"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 1905) is duplicated.

Oui, s’il vous plaît, supprimez les lignes en double.

@riking la corruption des index par PostgreSQL est un bug de PostgreSQL, pas de Discourse. Nous pouvons certes améliorer les performances de cette insertion, mais le bug de PostgreSQL doit être corrigé dans PostgreSQL.

Je suppose que cela est lié à une fermeture brutale du moteur de base de données, peut-être due à une perte d’alimentation.

C’est une explication plausible. Est-ce que ./launcher shutdown app (ou rebuild) effectue un arrêt propre de Postgres d’une manière ou d’une autre ? Ah, mais je parie qu’une mise à jour automatique ne sait pas comment procéder à un arrêt propre des conteneurs Docker, n’est-ce pas ?