Erreur de mise à niveau - index rake db:migrate sur theme_field_id

Dans les dernières notes de mise à jour (3.2.0.beta1), j’ai remarqué le plugin discourse-ai que je n’avais pas vu auparavant, j’ai donc tenté d’ajouter ce plugin et de mettre à jour mon instance discourse en même temps.

Comme mentionné dans le titre, je rencontre actuellement une erreur lors du bootstrap où rake db:migrate ne parvient pas à créer un index unique sur theme_field_id. Voici quelques détails sur la façon dont j’en suis arrivé là…

Tentative de mise à niveau initiale (erreur patch-package)

J’utilise une installation avec des conteneurs séparés, donc j’ai :

  • Modifié mon fichier web_only.yml pour ajouter le nouveau plugin discourse-ai

    par ex. Ajout d'une ligne supplémentaire aux hooks des plugins
    ## Les plugins vont ici
    ## voir https://meta.discourse.org/t/19157 pour les détails
    hooks:
      after_code:
        - exec:
            cd: $home/plugins
            cmd:
              - sudo -E -u discourse git clone https://github.com/discourse/docker_manager.git
              - sudo -E -u discourse git clone https://github.com/discourse/discourse-voting.git
              - sudo -E -u discourse git clone https://github.com/discourse/discourse-ai.git
    
  • Exécuté ./launcher bootstrap web_only

Cela a échoué avec un message indiquant que patch-package était introuvable.

Git Pull → bootstrap (erreur pg-vector)

J’ai pensé qu’il valait mieux m’assurer d’avoir les dernières mises à jour du lanceur avant de réessayer :

  • J’ai exécuté un git pull pour m’assurer d’avoir les dernières mises à jour liées au lanceur
  • J’ai réexécuté ./launcher bootstrap web_only

Cette fois, j’ai reçu des messages d’erreur liés à pg-vector.

:page_facing_up: Extrait du journal du bootstrap avec discourse-ai

J’ai noté mes versions PostgreSQL pour mes archives lorsque j’ai décidé de revisiter le plugin discourse-ai.

  • web_only :
    • client : psql (PostgreSQL) 13.10 (Debian 13.10-1.pgdg110+1)
  • data :
    • serveur : PostgreSQL 13.9 (Debian 13.9-1.pgdg110+1)

Suppression du plugin discourse-ai → Bootstrap

J’ai ensuite supprimé le plugin discourse-ai du fichier web_only.yml et j’ai relancé un bootstrap.

À ma grande surprise, je voyais toujours des erreurs, mais cette fois, elles semblaient liées à rake db:migrate qui ne parvenait pas à créer un index unique index_javascript_caches_on_theme_field_id avec le détail : Key (theme_field_id)=(3) is duplicated.

:page_facing_up: Extrait du journal du bootstrap sans discourse-ai

Votre aide ? :folded_hands:

C’est pourquoi je viens chercher de l’aide. J’ai pensé faire une pause et recueillir les avis de la communauté avant de creuser davantage, au cas où quelqu’un d’autre aurait déjà rencontré ce problème.

Pour référence, j’ai installé la version 3.2.0.beta1-dev (993ed10cf0 ~ 9 août).

Et bien que je ne pense pas que ce soit lié, je pense que cela ne fera pas de mal de mentionner que j’ai migré entre des machines hôtes au début de cette année… bien que j’aie effectué plusieurs mises à jour de Discourse via l’interface d’administration depuis lors.

Approche de migration

De mémoire, il s’agissait essentiellement de mettre à niveau l’instance source vers la dernière version de Discourse, d’installer Discourse sur le nouvel hôte, de geler la source, de faire une sauvegarde de Discourse sur la source, de synchroniser les images/etc entre les hôtes, puis de restaurer la sauvegarde sur le nouvel hôte.

Pour les erreurs d’index en double, je pense que si vous pouvez redémarrer web_only et les gérer depuis l’interface utilisateur, c’est beaucoup plus simple que de le faire dans la base de données. Bien que je ne pense pas en avoir déjà vu sur theme_field_id auparavant.

La ligne juste au-dessus de rake aborted! dans les journaux mentionne que discourse-voting est maintenant discourse-topic-voting. Pourriez-vous essayer de mettre à jour le lien dans la section des plugins vers celui actuel et voir si cela aide ?

Pouvez-vous clarifier quelle ressource est désignée comme ayant un ID en double ? Je ne savais pas exactement à quoi se réfère theme_field_id.

Pour information, je n’avais pas arrêté le conteneur web_only. Je démarre généralement le conteneur en premier pour minimiser la fenêtre d’interruption :

Je vais essayer. Je soupçonne que cela n’aidera pas car je pense que GitHub redirige silencieusement les choses en arrière-plan et que ce n’est qu’un avertissement du côté de Discourse. :slight_smile:

Si vous avez un site opérationnel avec l’explorateur de données installé, je pense que vous devriez pouvoir jeter un œil à ce à quoi cela pourrait se référer en utilisant cette requête :

SELECT *
FROM theme_fields
WHERE id = 3
1 « J'aime »

Je n’étais pas au courant de ce plugin - c’est génial ! Je ne l’ai pas installé, mais je suis capable d’entrer dans le conteneur de données et d’exécuter des requêtes.

C’est un excellent indice ! Si je lis correctement le message d’erreur, je ne pense pas que ce soit sur la table theme_fields car cette table particulière n’a pas de theme_field_id. Je n’ai pas vérifié le code source, mais je me risquerais à deviner que le premier argument de add_index() est le nom de la table et le second est la colonne.

Sur cette base, il semble que ce serait la table javascript_caches.

== 20230817174049 EnsureJavascriptCacheIsUniquePerTheme: migrating ===========
-- remove_index(:javascript_caches, :theme_id)
   -> 0.0208s
-- add_index(:javascript_caches, :theme_id, {:unique=>true})
   -> 0.0079s
-- remove_index(:javascript_caches, :theme_field_id)
   -> 0.0026s
-- add_index(:javascript_caches, :theme_field_id, {:unique=>true})

J’ai donc vérifié la structure de cette table et elle possède la colonne theme_field_id :

discourse=# \d javascript_caches
                                          Table "public.javascript_caches"
     Column     |            Type             | Collation | Nullable |                    Default
----------------+-----------------------------+-----------+----------+-----------------------------------------------
 id             | bigint                      |           | not null | nextval('javascript_caches_id_seq'::regclass)
 theme_field_id | bigint                      |           |          |
 digest         | character varying           |           |          |
 content        | text                        |           | not null |
 created_at     | timestamp without time zone |           | not null |
 updated_at     | timestamp without time zone |           | not null |
 theme_id       | bigint                      |           |          |
 source_map     | text                        |           |          |
Indexes:
    "javascript_caches_pkey" PRIMARY KEY, btree (id)
    "index_javascript_caches_on_digest" btree (digest)
    "index_javascript_caches_on_theme_field_id" btree (theme_field_id)
    "index_javascript_caches_on_theme_id" btree (theme_id)
Check constraints:
    "enforce_theme_or_theme_field" CHECK (theme_id IS NOT NULL AND theme_field_id IS NULL OR theme_id IS NULL AND theme_field_id IS NOT NULL)
Foreign-key constraints:
    "fk_rails_58f94aecc4" FOREIGN KEY (theme_id) REFERENCES themes(id) ON DELETE CASCADE
    "fk_rails_ed33506dbd" FOREIGN KEY (theme_field_id) REFERENCES theme_fields(id) ON DELETE CASCADE

J’ai pu interroger cette table (avec les champs de contenu raccourcis pour que je puisse vraiment le lire) et je peux voir les doublons. Mais je ne suis pas sûr des implications de ces doublons.

discourse=# select id, theme_field_id, digest, substring(content from 1 for 64) as content_64, created_at, updated_at, theme_id, substring(source_map from 1 for 64) as source_64 from javascript_caches where theme_field_id = 3;
 id | theme_field_id |                  digest                  |                            content_64                            |         created_at         |         updated_at         | theme_id |                            source_64
----+----------------+------------------------------------------+------------------------------------------------------------------+----------------------------+----------------------------+----------+------------------------------------------------------------------
  1 |              3 | d0b6ec642d5649064ff0501cadc775a9217b16e0 | "define"in window&&define("discourse/theme-3/initializers/theme- | 2019-02-25 01:26:56.606537 | 2023-08-18 20:47:19.596923 |          | {"version":3,"sources":["discourse/initializers/theme-field-3-co
  2 |              3 | 7fd74ecf4448afccdbcd9ccde87acddb4ec6f514 | "define"in window&&define("discourse/theme-3/initializers/theme- | 2019-02-25 01:26:58.228209 | 2023-08-18 20:50:41.049209 |          | {"version":3,"sources":["discourse/initializers/theme-field-3-co

Le contenu me semblait familier, alors je suis allé dans Personnaliser → Thème → Mon Thème → Commun → En-tête, j’ai fait une petite modification et j’ai sauvegardé, et je peux voir que l’une des entrées a été mise à jour et que l’une d’elles reste ancienne…

discourse=# select id, theme_field_id, digest, substring(content from 1 for 64) as content_64, created_at, updated_at, theme_id, substring(source_map from 1 for 64) as source_64 from javascript_caches where theme_field_id = 3;
 id | theme_field_id |                  digest                  |                            content_64                            |         created_at         |         updated_at         | theme_id |                            source_64
----+----------------+------------------------------------------+------------------------------------------------------------------+----------------------------+----------------------------+----------+------------------------------------------------------------------
  2 |              3 | 7fd74ecf4448afccdbcd9ccde87acddb4ec6f514 | "define"in window&&define("discourse/theme-3/initializers/theme- | 2019-02-25 01:26:58.228209 | 2023-08-18 20:50:41.049209 |          | {"version":3,"sources":["discourse/initializers/theme-field-3-co
  1 |              3 | 7f4132b1f9ced1b90b8f8fc24812cc11e81fea8d | "define"in window&&define("discourse/theme-3/initializers/theme- | 2019-02-25 01:26:56.606537 | 2023-09-13 21:56:56.312263 |          | {"version":3,"sources":["discourse/initializers/theme-field-3-co
(2 rows)

Encore une fois, je ne suis pas sûr des implications de ces doublons, s’il est sûr de supprimer l’ancien, ou s’il existe un autre moyen de “reconstruire” les caches qui nettoierait cela ?

Pour être honnête, j’en suis à 0/2 jusqu’à présent, donc je ne suis probablement pas la meilleure source d’information pour celui-ci. :slight_smile:

Il y a eu quelques sujets précédents où quelque chose de similaire s’est produit pour index_tags_on_name s’ils pouvaient être utiles ? par exemple.

1 « J'aime »

Reconstruisez votre conteneur de données. Le plugin d’IA nécessite une extension que vous n’avez pas encore.

J’apprécie votre aide ! Il semble que vous m’ayez mis sur la bonne voie. :star_struck:

J’ai sauvegardé la table avec pg_dump, supprimé l’ancienne entrée en double, et le démarrage s’est terminé avec succès. :+1:

Merci pour la confirmation ! Je pensais qu’il faudrait peut-être mettre à jour le conteneur de données (ou autrement installer pgvector). Je repoussais cela car je ne voulais pas avoir à gérer l’interruption de service.

D’après le fil de discussion discourse-ai, il semble que PG15 soit à l’horizon, donc je vais peut-être attendre un peu pour cela.

Il semble que la dépendance pgvector pourrait être pour la fonctionnalité Embeddings que je ne prévoyais pas d’utiliser, mais malheureusement, il semble que tout soit regroupé. Je voulais surtout jouer avec certaines des fonctionnalités de réécriture magique d’OpenAI / ChatGPT, donc c’est peut-être une raison de plus d’attendre la prochaine mise à jour majeure du conteneur de données. :slight_smile:

1 « J'aime »

Vous pouvez attendre si vous voulez, mais les utilisateurs de conteneur unique (ce qui représente la grande majorité des auto-hébergeurs) mettent à jour leur postgres potentiellement à chaque mise à niveau, il n’y a donc pas beaucoup de raisons d’attendre, à part quelques minutes supplémentaires d’indisponibilité. Vous devrez reconstruire (ou peut-être simplement détruire et redémarrer) le conteneur web_only après la reconstruction de la base de données.

1 « J'aime »

C’est la raison principale. Même si ce n’est que quelques minutes, je préférerais minimiser cela si ce n’est pas critique.

Dans cet ordre d’idées, je devrais probablement m’abstenir d’utiliser un plugin bêta pour le potentiel temps d’arrêt seul. :stuck_out_tongue:

J’ai suivi de près certaines des discussions sur les mises à niveau « sans temps d’arrêt ». Peut-être est-ce juste des lunettes roses rétrospectives sur l’histoire récente, mais j’ai l’impression d’avoir pu utiliser /admin/upgrade pour la plupart des mises à jour au cours de la dernière année, j’ai donc concentré mon attention sur des projets plus critiques pour le moment et j’ai perdu tout intérêt pour l’approche sans temps d’arrêt.

Lorsque j’ai migré vers un hébergement plus grand en janvier, c’était « sans temps d’arrêt » dans le sens où les utilisateurs pouvaient continuer à accéder au contenu du site, mais il y a eu une brève période où les choses étaient en lecture seule pendant la transition. Je suppose que je pourrais utiliser cette approche pour une mise à niveau majeure du conteneur data si je voulais vraiment minimiser le temps d’arrêt lors de la prochaine grande mise à niveau de data.

PS. J’ai lu énormément de vos publications dans la communauté ici au fil des ans. Merci pour tous vos efforts pour soutenir la communauté ! :star2:

C’est vraiment le cas ! Cela fait un moment que je n’ai pas effectué de mise à niveau pour mes clients qui « reconstruisent quand ils en ont besoin ». Pour les sites comme le vôtre, mon tableau de bord effectuera la reconstruction et les migrations post-mise à niveau après le lancement du nouveau conteneur (si votre conteneur existant a ces migrations configurées pour ne pas s’exécuter la première fois).

1 « J'aime »