Migration de la base de données vBulletin 5 - Erreurs de script d'importation

Oui, je vais faire ça si j’arrive à terminer au moins l’importation des utilisateurs. Actuellement, ça explose quand j’essaie de travailler sur les e-mails

Supprimez simplement la première ligne du script script/bulk_import/vbulletin5.rb
# frozen_string_literal: true

2 « J'aime »

Alors, en exécutant uniquement les trois premières fonctions :

 def execute
    # enable as per requirement:
    #SiteSetting.automatic_backups_enabled = false
    #SiteSetting.disable_emails = "non-staff"
    #SiteSetting.authorized_extensions = '*'
    #SiteSetting.max_image_size_kb = 102400
    #SiteSetting.max_attachment_size_kb = 102400
    #SiteSetting.clean_up_uploads = false
    #SiteSetting.clean_orphan_uploads_grace_period_hours = 43200
    #SiteSetting.max_category_nesting = 3

    import_groups
    import_users
    import_group_users

    #import_user_emails
    #import_user_stats
    #import_user_profiles
    #import_user_account_id

    #import_categories
    #import_topics
    #import_topic_first_posts
    #import_replies

    #import_likes

    #import_private_topics
    #import_topic_allowed_users
    #import_private_first_posts
    #import_private_replies

    #create_oauth_records
    #create_permalinks
    #import_attachments
  end

Résultat :

Je suppose que ce message concernant la garantie de la cohérence est destiné à être affiché lorsque l’importation complète est terminée ? Ou dois-je l’exécuter à chaque « étape » que j’exécute, puis faire une copie du répertoire discourse de l’hôte pour avoir une sauvegarde ?

1 « J'aime »

Le relancer avec les 4 fonctions suivantes actives renvoie une erreur pour des id déjà existants

Cela peut-il être un « tout ou rien » ? Peut-être s’attend-il à ce que tout soit fait en une seule grosse transaction ?

J’ai réessayé. Le processus s’est déroulé pendant un certain temps, puis soudainement ceci.

La partie frustrante est que cela semble sorti de nulle part.
Maintenant, le relancer aboutit à cette erreur.

Trop fatigué maintenant pour vérifier à quoi cela fait référence. Surtout parce que duplicate key value ne devrait vraiment pas arriver du tout si je relançais simplement le script d’importation en bloc, n’est-ce pas ??

Je vais commencer par m’excuser auprès de quiconque se sentirait attaqué par ce post car, pour être honnête, c’est depuis lundi que je me bats avec ces problèmes et à ce stade, je suis fatigué de faire du débogage/hotfix pour le code de Discourse.

Après la n-ième tentative (j’ai arrêté de compter après la 7ème), je pense que je vais abandonner car il semble que la migration ne soit pas quelque chose que Discourse ait beaucoup investi pour supporter.

Je crois que le plus gros problème est que le jeu de caractères utilisé dans cette énorme base de données est utf8mb4, ce qui n’est pas supporté par le script (?).

Utiliser utf8 (par défaut) génère simplement beaucoup d’erreurs qui sont signalées mais il n’est pas clair ce qui se passe car le script continue quand même. L’entrée dans la base de données est-elle ignorée ? Copiée avec des caractères non supportés (les carrés classiques) ?

De plus, les trois dernières exécutions différentes (en utilisant les importateurs en masse), avec exactement le même ensemble d’instructions suivies, ont des résultats différents. Cette dernière exécution a atteint l’importation des sujets, a immédiatement commencé à signaler des erreurs mais a continué (???):

Loading application...
Starting...
Preloading I18n...
Fixing highest post numbers...
Loading imported group ids...
Loading imported user ids...
Loading imported category ids...
Loading imported topic ids...
Loading imported post ids...
Loading groups indexes...
Loading users indexes...
Loading categories indexes...
Loading topics indexes...
Loading posts indexes...
Loading post actions indexes...
Importing categories...
Importing parent categories...
      5 -   1104/sec
Importing children categories...
    500 -   1539/secERROR:  duplicate key value violates unique constraint "unique_index_categories_on_name"
DETAIL:  Key (COALESCE(parent_category_id, '-1'::integer), name)=(-1, Armata Brancaleone) already exists.
CONTEXT:  COPY categories, line 69
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/pg-1.4.5/lib/pg/connection.rb:204:in `get_last_result'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/pg-1.4.5/lib/pg/connection.rb:204:in `copy_data'
/var/www/discourse/script/bulk_import/base.rb:720:in `create_records'
/var/www/discourse/script/bulk_import/base.rb:361:in `create_categories'
script/bulk_import/vbulletin5.rb:291:in `import_categories'
script/bulk_import/vbulletin5.rb:69:in `execute'
/var/www/discourse/script/bulk_import/base.rb:98:in `run'
script/bulk_import/vbulletin5.rb:779:in `<main>'
Importing topics...
    600 -   4073/sec
ERROR: undefined method `[]' for nil:NilClass
/var/www/discourse/script/bulk_import/base.rb:513:in `process_topic'
/var/www/discourse/script/bulk_import/base.rb:724:in `block (2 levels) in create_records'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/mysql2/alias_method.rb:8:in `each'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/mysql2/alias_method.rb:8:in `each'
/var/www/discourse/script/bulk_import/base.rb:721:in `block in create_records'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/pg-1.4.5/lib/pg/connection.rb:196:in `copy_data'
/var/www/discourse/script/bulk_import/base.rb:720:in `create_records'
/var/www/discourse/script/bulk_import/base.rb:364:in `create_topics'
script/bulk_import/vbulletin5.rb:321:in `import_topics'
script/bulk_import/vbulletin5.rb:70:in `execute'
/var/www/discourse/script/bulk_import/base.rb:98:in `run'
script/bulk_import/vbulletin5.rb:779:in `<main>'

Jusqu’à finalement planter sur celui-ci :

script/bulk_import/vbulletin5.rb:779:in `<main>'
 572329 -    531/sec
Importing replies...
client_loop: send disconnect: Connection reset

Mais pas avant de spammer constamment ces deux erreurs :

ERROR: undefined method `gsub!' for nil:NilClass
script/bulk_import/vbulletin5.rb:727:in `preprocess_raw'
script/bulk_import/vbulletin5.rb:369:in `block in import_topic_first_posts'
/var/www/discourse/script/bulk_import/base.rb:723:in `block (2 levels) in create_records'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/mysql2/alias_method.rb:8:in `each'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/mysql2/alias_method.rb:8:in `each'
/var/www/discourse/script/bulk_import/base.rb:721:in `block in create_records'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/pg-1.4.5/lib/pg/connection.rb:196:in `copy_data'
/var/www/discourse/script/bulk_import/base.rb:720:in `create_records'
/var/www/discourse/script/bulk_import/base.rb:367:in `create_posts'
script/bulk_import/vbulletin5.rb:361:in `import_topic_first_posts'
script/bulk_import/vbulletin5.rb:71:in `execute'
/var/www/discourse/script/bulk_import/base.rb:98:in `run'
script/bulk_import/vbulletin5.rb:779:in `<main>'

et

ERROR: invalid byte sequence in UTF-8
script/bulk_import/vbulletin5.rb:727:in `gsub!'
script/bulk_import/vbulletin5.rb:727:in `preprocess_raw'
script/bulk_import/vbulletin5.rb:369:in `block in import_topic_first_posts'
/var/www/discourse/script/bulk_import/base.rb:723:in `block (2 levels) in create_records'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/mysql2/alias_method.rb:8:in `each'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/mysql2/alias_method.rb:8:in `each'
/var/www/discourse/script/bulk_import/base.rb:721:in `block in create_records'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/pg-1.4.5/lib/pg/connection.rb:196:in `copy_data'
/var/www/discourse/script/bulk_import/base.rb:720:in `create_records'
/var/www/discourse/script/bulk_import/base.rb:367:in `create_posts'
script/bulk_import/vbulletin5.rb:361:in `import_topic_first_posts'
script/bulk_import/vbulletin5.rb:71:in `execute'
/var/www/discourse/script/bulk_import/base.rb:98:in `run'
script/bulk_import/vbulletin5.rb:779:in `<main>'

Veuillez noter que j’ai procédé étape par étape en commentant la fonction à exécuter, puis en exécutant rake import:ensure_consistency avant de continuer en commentant celles qui venaient d’être exécutées et ainsi de suite, car si je laisse le script entier réexécuter les étapes précédentes, il plante simplement en trouvant des ID dupliqués.

Avant que l’argument habituel “on ne peut pas se plaindre du logiciel gratuit” ne sorte, je tiens à préciser que je contribue à d’autres projets open source et que je crée également des logiciels gratuitement, mais il est primordial pour moi que si je publie quelque chose, cela fonctionne et soit bien documenté (ne serait-ce que pour éviter les milliers de messages demandant à juste titre “comment cela fonctionne”) ou je suis prêt à corriger tous les bugs qui surviennent.

Bien que Discourse semble offrir une excellente expérience prête à l’emploi, il doit être clair qu’en 2022, les communautés existaient bien avant ce produit. “L’adoption” nécessiterait un solide support de migration, ce qui ne semble pas être le cas actuellement pour Discourse.

Je reconnais qu’une base de données de 20 Go est un cas extrême, mais nous n’avons pas de problème de taille ici, plutôt de jeu de caractères ou de je-ne-sais-quoi car il n’y a même pas d’erreur constante et la plupart du temps, il n’y a pas de documentation à part devoir chercher des fils de discussion et des posts laissés par ceux qui sont passés par la même épreuve par le passé, en espérant qu’une solution de contournement ait été trouvée et que le code source n’ait pas beaucoup changé depuis.

À ce stade, je recommanderais fortement à quiconque venant de vBulletin de suspendre toute migration jusqu’à ce que ce qui semble être une refonte du script de migration (en cours, semble-t-il ?) soit terminée.

Bien que je ressente votre douleur (les migrations sont un sujet épineux), en tant que spécialiste des migrations chez Discourse, laissez-moi mettre les choses au clair.

Nous avons un cadre de migration mature avec plus de 60 scripts pour différentes plateformes, un cadre de masse séparé avec 5 scripts, et un nouveau cadre en cours de développement qui améliore massivement tous les aspects - performance, organisation du code, testabilité, vérifiabilité, documentation, etc.

Nous avons une équipe de migration séparée avec un soutien étendu des développeurs principaux, et nous contribuons aux améliorations génériques dans le code à chaque migration que nous terminons. Nous effectuons constamment des migrations pour des clients allant de triviales à incroyablement complexes.

Notre objectif final est de rendre les migrations aussi rationalisées que possible, tant pour les clients hébergés que pour la communauté, mais la quantité de code qui est dans le périmètre lors d’une migration est tout simplement trop massive, et la configuration logicielle au niveau du système, les changements de logiciels tiers et la variabilité des données d’entrée ne font qu’aggraver le problème.

Encore une fois, j’aimerais que tout cela soit moins douloureux, mais pour y parvenir, il faut d’innombrables heures de travail pour créer et maintenir, et il n’y en a qu’un nombre limité à disposition.

N’abandonnez pas ! :slight_smile:

5 « J'aime »

J’apprécie et je comprends que la portée est immense. C’est juste frustrant de tomber sur exception après exception et le fait que le projet soit écrit en Ruby n’aide pas à trouver de l’aide à part venir ici, ce qui, comme vous le dites, ne peut pas répondre à toutes les demandes d’aide car certains cas sont très spécifiques et impossibles à aider sans avoir accès aux données réelles.

Je rejette également, en grande partie, la faute sur le chaos absolu qu’est la structure de vBulletin.

Je viens de vérifier ce matin et voici un résumé des tailles des tables.

Pour donner du contexte, la table “text” contient le contenu réel.
La table “node” contient la hiérarchie et “closure”… laissez-moi citer car je n’en peux plus :

La table Closure construit les relations parent-enfant entre tous les nœuds. La majorité de votre base de données est constituée de fichiers joints qui ne devraient de toute façon pas être stockés dans la base de données.

Donc, globalement, pour un forum avec environ 8 Go de contenu, il y a une surcharge de 28 Go. Super, félicitations vBulletin.

2 « J'aime »

C’est ce que je veux dire quand je dis que c’est frustrant.

Encore une fois, même ensemble d’actions (en suivant un runbook écrit par moi-même avec toutes les tentatives et erreurs), exécuté sur une nouvelle installation de discourse.

Résultat :

Tired Tv Land GIF by TV Land Classic

Où es-tu import_user_account_id ? :expressionless:
Mais surtout ? Comment as-tu réussi à ne pas provoquer d’erreur lors de l’exécution précédente où cela a échoué lors de l’importation du sujet ? :confounded:

En commentant cet appel de fonction (qui semble pourtant important) et en relançant :

Ces erreurs de clés dupliquées… le script ne devrait-il pas savoir qu’il a déjà traité ces identifiants et passer à autre chose ?

Chaque importation est différente. On pourrait penser qu’un script qui fonctionne pour une instance de votre-forum-préféré fonctionnera simplement, mais ce n’est pas le cas. Et pour un forum énorme, c’est vraiment difficile. Ce n’est tout simplement pas quelque chose qui est facile à supporter. Et les importateurs en masse accèdent directement à la base de données plutôt que de compter sur Rails pour pouvoir vérifier automatiquement les choses au fur et à mesure.

C’est un problème fréquent, et ce n’est pas la faute du script. Vous devrez trouver comment migrer votre ancienne base de données vers utf8.

Il y a un fort support de migration. Il n’y a tout simplement pas de support de migration gratuit. J’ai effectué une centaine de migrations et écrit plusieurs scripts d’importation pour des systèmes non pris en charge ou sur mesure. Je facturerais probablement 3000 à 5000 $ pour importer votre base de données. Ce n’est pas une offre, c’est juste pour vous donner une idée de la quantité de travail que cela représente pour quelqu’un qui l’a fait plusieurs fois. Je suppose que si vous payiez une année d’hébergement Business, CDCK le ferait gratuitement, ce qui pourrait coûter moins cher que ce que je facturerais pour le faire. (Oh, mais vous pourriez ne pas être éligible à l’hébergement Business avec une base de données de cette taille).

1 « J'aime »

Je continue mon exploration ici.

  • Le script fait référence à une fonction qui n’existe pas : import_user_account_id. Vous (les développeurs de Discourse) voudrez peut-être corriger cela.
  • La logique qui vérifie les titres des sujets devient quelque peu folle à cause de certains sujets qui, pour une raison quelconque, ont une chaîne vide comme titre. Même si cela ne devrait pas arriver, la vérification qui évalue cela devrait la détecter et retourner nil mais apparemment, cela casse la logique de suivi écrite dans l’importation (voir ici).

J’ai eu ce problème avec une importation que j’ai faite récemment. Il serait préférable qu’il retourne quelque chose comme « le sujet XXX n’a pas de titre » ou qu’il récupère la première ligne de texte du message, mais c’est difficile à faire dans ce contexte. Je pense que ce que je serais tenté de faire, c’est de corriger votre base de données et d’utiliser autre chose pour générer des titres là où ils manquent.

Oui, je suis en train de « nettoyer » la base de données lorsque je trouve ces problèmes, mais c’est difficile car je dois déboguer le script et ensuite deviner ce qui cause le problème à chaque fois :sweat_smile:

Toujours aucune idée sur la fonction manquante import_user_account_id :expressionless:

Surtout que les fêtes approchent à grands pas, il est peu probable que quelqu’un corrige cela à moins qu’il n’utilise lui-même le script. (Habituellement, quand je dis ça, Richard arrive et sauve la situation.)

2 « J'aime »

LOL, eh bien, je suppose que je vais vous décevoir aujourd’hui. J’ai essayé, mais je soupçonne que la validation de cet importateur était incomplète et n’incluait pas certains changements à base.rb. @justin a travaillé là-dessus, peut-être qu’il sait. Je soupçonne que cela aurait pu être une chose spécifique au client qui peut être commentée sans conséquences supplémentaires.

Je n’ai jamais utilisé les importateurs en masse moi-même non plus.

Oui, les scripts d’importation peuvent être complexes et dépendent des spécificités de la base de données, mais certains scripts ne sont tout simplement pas fonctionnels. C’est le cas de celui-ci, et il y a d’autres scripts avec par exemple # frozen_string_literal: true qui ne fonctionnent tout simplement pas dès la sortie de la boîte.

2 « J'aime »

Ha !

C’est (au moins)

une partie de la raison pour laquelle je trouve si difficile de soumettre des PR pour les changements que j’apporte. Le temps que j’aie terminé, il y a tellement de choses spécifiques au cas là-dedans que j’ai peur que tout ce que je soumets soit cassé d’une manière ou d’une autre.

Ouais. Je pense que quelque chose est passé et a ajouté frozen_string_literal à tous les fichiers. La plupart des fichiers ont été corrigés car ils avaient des tests, mais il n’y a pas de tests pour les scripts d’importation.

2 « J'aime »

Salut, juste pour clarifier, je n’attends pas que quelqu’un corrige cela maintenant (style Karen). Je signale simplement certaines choses qui ont clairement des problèmes dans le code lui-même et qui sont probablement juste des « oups, j’ai oublié d’ajouter ce changement au commit ! :sweat_smile: »

J’ai déjà accepté que cette migration n’aura pas lieu avant janvier, au mieux, à ce stade.

Tout le monde devrait simplement profiter des fêtes :slight_smile:
J’aborderai ce sujet ou ouvrirai un nouveau fil après les fêtes, même si j’aurai certainement moins de temps à consacrer à cette migration :frowning:

2 « J'aime »

Ouais, c’est tout à fait vrai - pour les importations, je ne soumets généralement pas de PR avant d’avoir effectué deux importations de clients différents.

1 « J'aime »

Je mets juste à jour ceci.

J’ai apporté quelques modifications à base.rb après en avoir discuté avec d’autres ingénieurs de notre communauté. De nombreuses erreurs étaient causées par gsub! qui échouait car apparemment nous avions des sujets avec '' comme titre.

Nous avons ajouté une fonction imitant la fonction normalize_text qui retourne simplement l’imported_id du fil de discussion s’il n’y a pas de contenu, converti en chaîne de caractères.

  def normalize_text_thread(text, imported_id)
    return imported_id.to_s unless text.present?
    @html_entities.decode(normalize_charset(text.presence || "").scrub)
  end

Ensuite, dans vbulletin5.rb, j’ai modifié la ligne dans create_topic en :

create_topics(topics) do |row|
      created_at = Time.zone.at(row[5])

      title = normalize_text_thread(row[1], row[0])

Cela a résolu le problème. Essentiellement, gsub! ne gère pas bien la réception d’un nil en entrée.

Cependant, cela a permis au script de continuer mais lorsqu’il a atteint import_private_topics, il s’est bloqué. Il y a 253 427 sujets privés (pm) dans notre base de données, ce qui est plusieurs ordres de grandeur de moins que les réponses. Après 9 heures, j’ai arrêté le script pour voir ce qui se passait réellement.

En lançant l’interface, j’ai remarqué plusieurs choses.

  1. Mon compte n’a pas été importé car l’utilisateur administrateur créé utilisait la même adresse e-mail, je suppose. C’est évident mais quelque chose qui devrait peut-être être écrit quelque part ?
  2. Seules certaines des catégories (sous-forums vbulletin) ont été importées.
  3. Seuls les sujets et leur première réponse ont été importés (je ne suis pas sûr si tous vraiment) et ils ont tous été importés sans être dans les bonnes catégories, même ceux qui avaient une catégorie qui aurait dû être créée. Tout est importé “sans catégorie”.
  4. Le “compteur de réponses” affiche -1, probablement parce que les réponses n’ont en fait pas été importées du tout.

J’ajouterai dans l’ensemble, BEAUCOUP de problèmes avec cet import en masse disparaîtraient s’il implémentait une approche de pagination. Je pense que les réponses ont disparu parce que le script a essayé de les parcourir toutes en même temps et avec 7 Go de données, c’était impossible. Cela me laisse perplexe qu’un importateur en masse n’aborde pas l’importation avec une approche de pagination pour être honnête. Même prendre simplement 1000 enregistrements à la fois, les écrire et stocker le dernier ID d’enregistrement écrit et boucler résoudrait tous les problèmes avec les grandes bases de données.

1 « J'aime »

Pour information, je suis cette discussion avec intérêt et j’apprécie beaucoup les mises à jour. :pray: Je ne connais pas grand-chose aux migrations jusqu’à présent, mais je trouve cela très instructif.

4 « J'aime »