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

Ok, récapitulons rapidement.

Je fais du bénévolat pour la migration d’un forum qui est actuellement sur vbulletin3.
Sur un environnement de staging, en partant d’une sauvegarde de la base de données (20 Go, vous avez bien lu).

Lancement de la mise à niveau vers vBulletin 5. Cela a pris 5-6 heures mais s’est bien passé. La version est vBulletin 5.4.
J’ai effectué un nettoyage des noms d’utilisateur pour qu’ils soient acceptés par discourse.

Maintenant, j’ai installé docker discourse et j’ai suivi plus ou moins ce guide pour la préparation. Plus ou moins, car la plupart des informations étaient redondantes ou obsolètes, mais cela m’a aidé à avoir une idée de ce qu’il fallait faire.

Je suis à l’étape où je suis littéralement en train de perdre la vue car je n’ai quasiment aucune expérience en codage Ruby.
Donc, les parties pertinentes, après avoir terminé l’installation, je suis entré dans le conteneur avec ./launcher enter app puis :

  • Ajout de freetds-dev et libmariadb-dev
  • Modification du Gemfile pour ajouter la gem php_serialize.
  • Depuis le shell, exécution de export IMPORT=1 pour définir l’environnement d’importation.
  • En tant qu’utilisateur discourse, exécution de bundle install --no-deployment --without test --without development --path vendor/bundle.

J’ai obtenu l’erreur :

You are trying to install in deployment mode after changing
your Gemfile. Run `bundle install` elsewhere and add the
updated Gemfile.lock to version control.

If this is a development machine, remove the /var/www/discourse/Gemfile freeze
by running `bundle config unset deployment`.

The list of sources changed
The dependencies in your gemfile changed

You have added to the Gemfile:
* mysql2
* redcarpet
* php_serialize
* sqlite3 (~> 1.3, >= 1.3.13)
* ruby-bbcode-to-md
* reverse_markdown
* tiny_tds
* csv
* parallel

Donc, j’ai continué avec :

  • bundle config unset deployment et j’ai relancé la commande précédente.
  • J’ai vérifié que mysql2 et php_serialize étaient bien présents (ils l’étaient).
  • J’ai ajouté les anciens avatars du forum (pas de pièces jointes à importer) et j’ai attribué la propriété des répertoires à l’utilisateur discourse dans son propre répertoire /home/discourse.
  • J’ai modifié script/import_scripts/vbulletin5.rb pour changer la référence de connexion à la base de données.
  • En tant qu’utilisateur discourse, j’ai exécuté bundle exec ruby script/import_scripts/vbulletin5.rb.

Cela m’a retourné une erreur concernant tzinfo Integer values not supported que j’ai trouvée mentionnée ici sur ce discourse.

Loading existing groups...
Loading existing users...
Loading existing categories...
Loading existing posts...
Loading existing topics...

importing groups...
       41 / 41 (100.0%)  [2294 items/min]  ]
importing users
Traceback (most recent call last):
        15: from script/import_scripts/vbulletin5.rb:726:in `<main>'
        14: from /var/www/discourse/script/import_scripts/base.rb:47:in `perform'
        13: from script/import_scripts/vbulletin5.rb:46:in `execute'
        12: from script/import_scripts/vbulletin5.rb:79:in `import_users'
        11: from /var/www/discourse/script/import_scripts/base.rb:916:in `batches'
        10: from /var/www/discourse/script/import_scripts/base.rb:916:in `loop'
         9: from /var/www/discourse/script/import_scripts/base.rb:917:in `block in batches'
         8: from script/import_scripts/vbulletin5.rb:98:in `block in import_users'
         7: from /var/www/discourse/script/import_scripts/base.rb:264:in `create_users'
         6: from /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'
         5: from /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'
         4: from /var/www/discourse/script/import_scripts/base.rb:265:in `block in create_users'
         3: from script/import_scripts/vbulletin5.rb:110:in `block (2 levels) in import_users'
         2: from script/import_scripts/vbulletin5.rb:718:in `parse_timestamp'
         1: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/tzinfo-2.0.5/lib/tzinfo/timezone.rb:575:in `utc_to_local'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/tzinfo-2.0.5/lib/tzinfo/timestamp.rb:138:in `for': Integer values are not supported (ArgumentError)

La suggestion de @Haddoq était de changer une ligne de Time.zone.at(@tz.utc_to_local(timestamp)) en Time.zone.at(timestamp).

Elle suggère également d’ajouter lastvisit dans la requête utilisateur car sinon cela provoquera une autre erreur, ce que j’ai également fait.

Cependant, maintenant, lorsque je lance la migration avec bundle exec ruby script/import_scripts/vbulletin5.rb, voici ce que j’obtiens :

Loading existing groups...
Loading existing users...
Loading existing categories...
Loading existing posts...
Loading existing topics...

importing groups...
       41 / 41 (100.0%)  [120217 items/min]
importing users
Traceback (most recent call last):
        13: from script/import_scripts/vbulletin5.rb:727:in `<main>'
        12: from /var/www/discourse/script/import_scripts/base.rb:47:in `perform'
        11: from script/import_scripts/vbulletin5.rb:46:in `execute'
        10: from script/import_scripts/vbulletin5.rb:79:in `import_users'
         9: from /var/www/discourse/script/import_scripts/base.rb:916:in `batches'
         8: from /var/www/discourse/script/import_scripts/base.rb:916:in `loop'
         7: from /var/www/discourse/script/import_scripts/base.rb:917:in `block in batches'
         6: from script/import_scripts/vbulletin5.rb:80:in `block in import_users'
         5: from script/import_scripts/vbulletin5.rb:723:in `mysql_query'
         4: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/mysql2/alias_method.rb:22:in `query'
         3: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/mysql2-0.5.4/lib/mysql2/client.rb:147:in `query'
         2: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/mysql2-0.5.4/lib/mysql2/client.rb:147:in `handle_interrupt'
         1: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/mysql2-0.5.4/lib/mysql2/client.rb:148:in `block in query'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/mysql2-0.5.4/lib/mysql2/client.rb:148:in `_query': You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'CASE WHEN u.scheme='blowfish:10' THEN token (Mysql2::Error)
                 WHEN u.scheme='lega' at line 2

À ce stade, je suis un peu perdu. Quelqu’un peut-il m’aider ?

paging @Canapin car il a été dans « Migration Vietnam » et pourrait en savoir plus :heart:

Travailler 8 heures d’affilée est une mauvaise idée.

En ajoutant u.lastvisit à la fin de SELECT u.userid, u.username, u.homepage, u.usertitle, u.usergroupid, u.joindate, u.email,, j’ai oublié d’ajouter une virgule après.

Désolé Canapin pour le ping :frowning:

1 « J'aime »

D’accord, problème de suivi. Le guide que je suivais indiquait que le processus d’enregistrement pouvait être redémarré s’il ralentissait.

Mais lorsque je le redémarre, j’obtiens des erreurs indiquant que les utilisateurs existent déjà dans la base de données postgres.

Stupide que je suis, j’ai supprimé tous les utilisateurs avec id > 1 dans la base de données (en laissant essentiellement le discobot, le système et l’administrateur) et j’ai redémarré. Cela permet à l’importation de continuer, mais les e-mails de tous les utilisateurs précédemment créés sont marqués comme « déjà utilisés » quelque part.

Que puis-je faire pour nettoyer et ce processus ne devrait-il pas plutôt ignorer l’insertion s’il existe déjà un nom d’utilisateur correspondant ?

Modification : D’accord, j’ai découvert que je devais vider users, email_tokens et user_emails.

1 « J'aime »

Malheureusement, je n’ai pas suivi ce que j’ai modifié dans mes importations précédentes, donc j’ai peu de connaissances. J’ai maintenant une instance Discourse personnelle où j’écris ce genre de choses… J’aurais dû le faire avant !
Je suis plus compétent en matière d’importation lorsque je travaille sur une importation.

Quant à la suppression d’utilisateurs, vous auriez peut-être voulu utiliser UserDestroyer via la console Rails :

2 « J'aime »

Super, je garde ça en tête pour la « vraie » migration. Pour l’instant, je fais juste un test de l’ensemble du processus tout en rédigeant un runbook :slight_smile:

1 « J'aime »

Pourquoi as-tu fait ça ? À moins que tu n’aies changé quelque chose dans le script qui importerait ceux-ci différemment, tu devrais juste redémarrer et le laisser importer ceux qui ne sont pas encore importés.

Si tu as besoin de recommencer, il est beaucoup plus facile de restaurer une sauvegarde ou de supprimer et de créer une nouvelle base de données.

Il devrait trouver les identifiants d’importation dans la table UserCustomFields. Je ne suis pas tout à fait sûr de la façon dont tu pourrais obtenir cette erreur.

Les changements sont ceux que j’ai énumérés. Je suis aussi surpris que vous, mais le script n’avançait pas et se bloquait simplement avec une erreur.

Y a-t-il un moyen d’accélérer l’importation ?

J’ai plus de 90 000 utilisateurs dans cette communauté et la vitesse d’importation se dégrade avec le temps pour une raison quelconque et je n’arrive pas à imaginer pourquoi.

Elle a fonctionné toute la nuit et juste pour les utilisateurs, nous en sommes à 25123 / 95635 ( 26.3%) [42 éléments/min]

Il y a plusieurs ordres de grandeur de messages en plus. Combien de temps dois-je m’attendre à ce qu’une migration prenne ? Des jours ? Des semaines ?

Combien de RAM ? C’est probablement le problème. Vous pouvez arrêter et redémarrer.

J’en ai eu qui ont pris des semaines. C’est pourquoi les importateurs en masse existent.

Il n’a que 2 Go de RAM. C’est une machine de test. Je pourrais l’exécuter localement au lieu d’une VM (16 Go de RAM seraient tellement mieux ?) puis tout empaqueter et télécharger éventuellement, je suppose.

Pouvez-vous développer sur les importateurs en masse ? C’est la première fois que j’en entends parler et cela aurait certainement dû apparaître lorsque je cherchais « migrer vbulletin vers discourse » sur Google :angry:

Frustrated Jason Segel GIF by NETFLIX

Puis-je l’exécuter même si l’autre importateur a déjà traité certains utilisateurs ou dois-je nettoyer ?

J’ai essayé, au pire ça ne fonctionnera pas. Je suis spammé par

ERROR: no implicit conversion of nil into String
/var/www/discourse/script/bulk_import/base.rb:861:in `encode'
/var/www/discourse/script/bulk_import/base.rb:861:in `normalize_charset'
/var/www/discourse/script/bulk_import/base.rb:856:in `normalize_text'
script/bulk_import/vbulletin5.rb:123:in `block in import_users'
/var/www/discourse/script/bulk_import/base.rb:725: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:723: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:722:in `create_records'
/var/www/discourse/script/bulk_import/base.rb:340:in `create_users'
script/bulk_import/vbulletin5.rb:120:in `import_users'
script/bulk_import/vbulletin5.rb:63:in `execute'
/var/www/discourse/script/bulk_import/base.rb:100:in `run'
script/bulk_import/vbulletin5.rb:781:in `<main>'

Je suppose que je devrais nettoyer les utilisateurs et les groupes qui sont les seules choses que l’autre importateur a créées/a commencé à créer.

Avant de me lancer à nouveau dans la base de données, existe-t-il une commande ruby que je peux exécuter pour s’en occuper de manière propre ?

Pour cette fois, je vais simplement détruire et recréer l’installation de discourse. Dieu merci pour Docker.

Non, installation propre, le script échoue toujours avec ce message générique

ERREUR : aucune conversion implicite de nil en String
/var/www/discourse/script/bulk_import/base.rb:861:in `encode'
/var/www/discourse/script/bulk_import/base.rb:861:in `normalize_charset'
/var/www/discourse/script/bulk_import/base.rb:856:in `normalize_text'
script/bulk_import/vbulletin5.rb:123:in `block in import_users'
/var/www/discourse/script/bulk_import/base.rb:725: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:723: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:722:in `create_records'
/var/www/discourse/script/bulk_import/base.rb:340:in `create_users'
script/bulk_import/vbulletin5.rb:120:in `import_users'
script/bulk_import/vbulletin5.rb:63:in `execute'
/var/www/discourse/script/bulk_import/base.rb:100:in `run'
script/bulk_import/vbulletin5.rb:781:in `<main>'

Y a-t-il un moyen de déboguer cela afin que je puisse au moins comprendre quelle valeur est nil et est censée être autre chose ?

Ok, l’erreur semble liée à l’encodage.
Dans bulk_import/vbulletin5.rb, je peux spécifier l’encodage (dans notre cas, UTF8mb4 mais dans le fichier base.rb, il ne semble pas correspondre à quoi que ce soit dans la carte des jeux de caractères

Salut !

Quelques conseils généraux :

  1. Si vous apportez des modifications au script, il est généralement conseillé de recommencer à zéro ou au moins à partir d’un point de départ connu. Vous pouvez le faire le plus facilement en restaurant une sauvegarde effectuée juste avant d’exécuter la migration, comme l’a dit @pfaffman.
  2. Les importateurs en masse prendront généralement beaucoup moins de temps, mais celui-ci en particulier a le potentiel de consommer ÉNORMÉMENT de RAM car il met en cache des éléments en mémoire. Pour une migration vBulletin en masse que j’ai effectuée à partir d’un fichier SQL de 2 Go non compressé, le processus a nécessité 22 Go de RAM (double vérification, pas une faute de frappe).
  3. Si vous apportez des modifications au script, je vous suggère de créer une version de test de l’entrée avec, disons, 100 ou 1000 enregistrements pour chaque table (mais faites attention à l’intégrité référentielle - c’est-à-dire ne tronquez pas les tables référencées par d’autres tables). Tester des modifications avec un processus de plus de 8 heures érodera votre santé mentale très rapidement.

Un conseil plus spécifique concernant les traces de pile : recherchez les lignes mentionnant le fichier spécifique que vous avez exécuté. Dans ce cas, oui, il s’agit d’un problème d’encodage, mais plus pertinent est le fait qu’il concerne les noms d’utilisateur :

Vous avez dit que vous deviez assainir les noms d’utilisateur, je vérifierais donc que vous les avez encodés comme prévu par le script.

2 « J'aime »

Vous pouvez simplement supprimer, créer et migrer la base de données plutôt que de recréer Discourse. C’est un peu délicat, cependant, car vous devez

  sv stop unicorn

puis

  rake db:drop db:create db:migrate

Cela se plaindra et vous dira de définir une variable d’environnement que vous devez placer sur la ligne avant la tâche rake.

Vous pouvez également restaurer une sauvegarde, ce qui peut être plus pratique.

Pour information, je ne pense pas avoir jamais utilisé de script de migration en masse.

1 « J'aime »

C’est encore plus étrange car les noms d’utilisateur ont tous été assainis pour suivre les directives de Discourse, en gros, ils ont tous été changés pour n’être que des lettres, des chiffres ou _, rien d’autre.

Dans tous les cas, changer le jeu de caractères de utf8mb4 à utf8 comme c’était le cas par défaut a permis de continuer, mais je reçois ensuite des erreurs pour des e-mails invalides.

ERROR: can't modify frozen String: "24ef401b30f5161e5a0bb27ec49ed921@email.invalid"
/var/www/discourse/script/bulk_import/base.rb:457:in `downcase!'
/var/www/discourse/script/bulk_import/base.rb:457:in `process_user_email'
/var/www/discourse/script/bulk_import/base.rb:726: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:723: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:722:in `create_records'
/var/www/discourse/script/bulk_import/base.rb:351:in `create_user_emails'
script/bulk_import/vbulletin5.rb:151:in `import_user_emails'
script/bulk_import/vbulletin5.rb:66:in `execute'
/var/www/discourse/script/bulk_import/base.rb:100:in `run'
script/bulk_import/vbulletin5.rb:781:in `<main>'

Je vais maintenant découvrir de quoi il s’agit car l’importation “non en masse” détectait des e-mails malformés mais les remplaçait automatiquement.

Une fois que vous l’avez fait, créez simplement une sauvegarde de votre installation Discourse vierge et vide.
Vous pourrez toujours la restaurer très rapidement et recommencer à zéro.

2 « J'aime »

Pour ajouter à cela, si vous rencontrez des problèmes, par exemple, avec l’importation des publications, vous pouvez également quitter le script une fois que l’importation des utilisateurs est terminée de manière satisfaisante et créer une autre sauvegarde. Ensuite, vous pouvez redémarrer le script et reprendre au moment où vous avez déjà importé les utilisateurs.

1 « J'aime »