Migration eines vBulletin 4 Forums zu Discourse

Ich bin erst seit kurzem ein Discourse-Nutzer, daher habe ich nach viel Hin und Her alles oben Genannte in eine vollständige schrittweise Anleitung umgewandelt (danke an @titusca und @enigmaty).

Ich hoffe, das hilft (oder beschleunigt zumindest) anderen Neueinsteigern, den gesamten Prozess von Anfang bis Ende zu durchlaufen. Ich würde dies gerne in den ersten Beitrag aufnehmen, da die Aktualisierungen von MySQL zu MariaDB meiner Meinung nach für viel Verwirrung im Prozess gesorgt haben.

Hintergrund:

  • Übertragung von 1,6 Millionen Beiträgen.
  • Genutzter Digital Ocean Droplet (CPU-optimiert: 4 vCPU/8 GB)

#1 – Digital Ocean Discourse 1-Klick-Droplet installieren

#2 – Discourse-Installation über SSH abschließen, indem Sie den Aufforderungen folgen

SSH-Konsole öffnen
root
(Ihr Root-Passwort)
(Enter)
(IhreDomain).com
(usw…)

#3 – An SFTP anmelden, um Datenbank-Dump hochzuladen

sftp root@XXX.XXX.XX.XX
y
yes
(Ihr Root-Passwort)
put db.sql /var/discourse/shared/standalone/db.sql

#4 – An der neuen Discourse-Website anmelden, um das Admin-Konto einzurichten

#5 – An SSH anmelden – Prozess beginnen

ssh root@XXX.XXX.XX.XX
cd /var/discourse
./launcher start app
docker exec -it app bash
sudo apt-get update
sudo apt-get upgrade
y

#6 – MariaDB installieren (Ersatz für MySQL)

apt-get update && apt-get install mariadb-server-10.3 libmariadbd-dev
y

#7 – MySQL-Datenbank einrichten

service mysql start
mysql -u root -p
Passwort
create database vbulletin;
exit;

#8 – Vbulletin → MySQL-Datenbankübertragung

mysql -u root -p vbulletin < /shared/db.sql
Passwort

#9 – GEM-Datei

echo “gem ‘mysql2’” >> Gemfile
echo “gem ‘mysql2’, require: false” >> /var/www/discourse/Gemfile
echo “gem ‘php_serialize’, require: false” >> /var/www/discourse/Gemfile
cd /var/www/discourse
su discourse -c ‘bundle install --no-deployment --without test --without development --path vendor/bundle’
(Roten Text ignorieren)

#10 – Installationsskript konfigurieren

vi /var/www/discourse/script/import_scripts/vbulletin.rb

#10.a – Textdatei nach Bedarf bearbeiten

DB_HOST ||= ENV[‘DB_HOST’] || “localhost”
DB_NAME ||= ENV[‘DB_NAME’] || “vbulletin”
DB_PW ||= ENV[‘DB_PW’] || “password”
DB_USER ||= ENV[‘DB_USER’] || “root”
TIMEZONE ||= ENV[‘TIMEZONE’] || “America/Los_Angeles”
TABLE_PREFIX ||= ENV[‘TABLE_PREFIX’] || “”
ATTACHMENT_DIR ||= ENV[‘ATTACHMENT_DIR’] || ‘/shared/attachments/’

#10.c – Bearbeitung abschließen

:wq

#11 – Bundle konfigurieren

bundle config set path ‘vendor/bundle’
bundle config set without ‘development:test’
bundle config unset deployment
su discourse -c ‘bundle install’

#12 – MySQL-Konfiguration (möglicherweise bereits mit dem vorherigen Schritt erledigbar)

mysql --version
sudo mysql -u root -p
Passwort
ALTER USER 'root'@'localhost' IDENTIFIED BY 'password';
FLUSH PRIVILEGES;
exit

#13 – Installationsskript ausführen

su discourse -c ‘bundle exec ruby script/import_scripts/vbulletin.rb’

Viel Erfolg!

8 „Gefällt mir“

Ich wollte einfach ein Feedback nach unserer Migration von vB4 hinterlassen:

  • FIXED [s]Soft-gelöschte Beiträge wurden nicht korrekt ausgeblendet: https://github.com/discourse/discourse/pull/12057[/s]
  • [ul] + [li] und verschachtelte [LIST] wurden nicht korrekt migriert, und das BBCode-Plugin scheint dies ebenfalls nicht zu handhaben → Dies scheint erwartet zu sein: CommonMark testing started here! (Zitat: Core wird keine Unterstützung für [ul], [ol] und [li] im BBCode implementieren, da dies ein Rezept für Misserfolg ist.) → Ich werde dafür einige Regex-Tricks für eine nachträgliche Bereinigung benötigen.
  • Wir haben eine erste Migration mit dem normalen Importer durchgeführt (dauerte > 3 Tage) und die Migration mit neueren DB-Snapshots ein paar Mal neu gestartet, um den Import „frisch
2 „Gefällt mir“

Leider kann ich meinen vorherigen Beitrag nicht bearbeiten :slight_smile:

Ich habe ein weiteres Problem festgestellt: Jeder Anhang, der nicht in einem Beitrag verlinkt ist, steht Discourse nicht zur Verfügung.

Mein Entwurf für einen Pull Request zur Behebung dieses Problems: FIX: vBulletin importer should import unreferenced attachments by paresy · Pull Request #12187 · discourse/discourse · GitHub

Vielen Dank!

3 „Gefällt mir“

Nur ein kurzes Follow-up zu meiner Problemliste. Ich habe das Sichtbarkeitsproblem behoben.

Dumpen Sie alle betroffenen Beiträge aus Ihrer alten vBulletin-Datenbank:

SELECT postid
FROM `vb4_post`
WHERE `visible` > '1'
ORDER BY postid

Erstellen Sie eine Datei namens imported_post_ids.txt, die alle postid-Werte zeilenweise enthält.

Erstellen Sie eine neue Datei für das Korrekturskript:

nano script/import_scripts/fix_visibility.rb 

Inhalt:

require_relative '../../config/environment'
require_relative 'base/lookup_container'

@lookup = ImportScripts::LookupContainer.new

broken_postids = []
broken_real_postids = []

File.foreach("imported_post_ids.txt") do |line|
  broken_postids.append(line.to_i)
end

broken_postids.each do |id|
  broken_real_postids.append(@lookup.post_id_from_imported_post_id(id))
end

broken_real_postids.each do |id|
  puts id
  Post.find(id).trash!
end

Führen Sie das Skript aus:

su discourse -c 'bundle exec ruby script/import_scripts/fix_visibility.rb'

Das Skript verwendet die Logik des Importers, um die importierten postid-Werte auf die entsprechenden Discourse-postid-Werte abzubilden, die wir ausblenden möchten.

4 „Gefällt mir“

Hallo Leute,

Ich lasse das Skript auf eine vb3-Migration laufen. Ich mache einen Schritt nach dem anderen und es verarbeitet derzeit 122.000 Benutzer mit 330/Minute. Dann müssen wir noch 2,5 Millionen Beiträge durchgehen.

Wir machen das auf einem Produktionsserver. Niemand benutzt die Discourse-Seite, wir haben sie gerade eingerichtet und sie hat eine anonyme URL. Wenn ich mich anmelde, sehe ich, wie die Benachrichtigungen für neue Benutzer zunehmen. Wahrscheinlich eine dumme Frage, aber ich frage mich, ob die Migration schneller ablaufen würde, wenn wir die Live-Seite irgendwie sperren oder deaktivieren würden?

1 „Gefällt mir“

Das hängt von der Auslastung und der Anzahl der CPUs auf Ihrem Produktionsserver ab. Sie können immer versuchen, den Webserver für 5 Minuten zu stoppen und zu sehen, ob der Import schneller geht.

3 „Gefällt mir“

Der Import dauert wirklich eine Weile. Soweit ich weiß, sollte der Massenimport schneller sein. Wir haben einen ersten Import aus einem Backup auf unserer leistungsstarken Entwicklungsmaschine durchgeführt und dann einen inkrementellen Import aus einem anderen Backup vorgenommen, um den Wechsel zu Discourse mit nur einer halben Stunde Ausfallzeit zu vollziehen. Vorsicht vor Dingen, die bei inkrementellen Updates schiefgehen können :slight_smile: (Siehe hier: Migrate a vBulletin 4 forum to Discourse - #132 by paresy)

paresy

3 „Gefällt mir“

Ich sehe einen Kern, der ausgelastet ist, was meiner Meinung nach der Server ist, der die aktualisierten Daten aufnimmt, und einen weiteren Kern, der ausgelastet ist, wenn das Importskript ausgeführt wird. Ich habe nicht genügend Fachwissen, um zu wissen, ob die Konkurrenz zwischen diesen beiden Prozessen um die Datenbankressource den Importeur verlangsamen könnte, und ich habe auch nicht genügend Fachwissen, um zu wissen, ob es überhaupt möglich ist, die Aufnahme zu stoppen, während der Container in Betrieb bleibt. Die Aufnahme muss sowieso erfolgen, daher nehme ich an, das Sicherste ist, sie einfach weiterlaufen zu lassen.

Ein Tipp für zukünftige Leser: Ich sehe, dass 27.000 (22 %!) unserer Benutzer gesperrte Spambots sind. Wir werden sie auf der Quellseite bereinigen, bevor wir den endgültigen Import durchführen.

[hinzugefügt] Eine notwendige Bearbeitung, die oben nicht erwähnt wird:

--- a/script/import_scripts/vbulletin.rb
+++ b/script/import_scripts/vbulletin.rb
@@ -134,6 +133,7 @@ EOM
        , usertitle
        , usergroupid
        , joindate
+       , lastvisit
        , email
        , password
        , salt

Und eine Bearbeitung, die vb3-spezifisch sein könnte:

--- a/script/import_scripts/vbulletin.rb
+++ b/script/import_scripts/vbulletin.rb
@@ -987,7 +989,7 @@ EOM
   end

   def parse_timestamp(timestamp)
-    Time.zone.at(@tz.utc_to_local(timestamp))
+    Time.zone.at(@tz.utc_to_local(Time.at(timestamp)))
   end

[hinzugefügt] Der Import läuft auf einer Oracle Cloud 4-Kern-Ampere-Instanz. Zum Vergleich habe ich lokal/nativ einen Discourse-Entwicklungsserver auf einem M1 MacBook Air installiert und war überrascht, dass der Importprozess deutlich langsamer lief.

6 „Gefällt mir“

Gab es Fehler mit dem vorhandenen Skript? Ich habe die Datums- und Zeitinformationen aller unserer alten vBulletin 4-Beiträge verloren, weil das passiert ist. Wenn dies eine Korrektur ist, würde ich gerne wissen, ob eine Re-Importierung eine gute Idee wäre, wenn alle Beiträge übernommen wurden.

2 „Gefällt mir“

Ja, das Skript würde einen Fehler ausgeben, da es eine Ganzzahl an eine Zeitfunktion übergibt.

3 „Gefällt mir“

Nein. Das Skript überspringt bereits importierte Beiträge.

3 „Gefällt mir“

Hallo,

Haben Sie herausgefunden, wie Sie das beheben können?

Unsere beiden Haupt-/unteren Foren haben parentid = -1 (ich glaube, das liegt daran, dass wir damals von v3 konvertiert haben).

Ich bin mir nicht sicher, wie ich vorgehen soll. Soll ich sie im Konvertierungsskript einfach auf 0 setzen, wenn -1 vorhanden ist? Angenommen, 0 ist die Hauptdiskussionskategorie?

Eigentlich, wenn ich mir jetzt die Diskussionsseite ansehe; diese beiden scheinen die einzigen zu sein, die importiert wurden?

 importing top level categories...
         2 / 2 (100.0%)  [211 items/min]  in]
 importing children categories...
 Traceback (most recent call last):
         5: from script/import_scripts/vbulletin.rb:1003:in `<main>'
         4: from /var/www/discourse/script/import_scripts/base.rb:47:in `perform'
         3: from script/import_scripts/vbulletin.rb:84:in `execute'
         2: from script/import_scripts/vbulletin.rb:287:in `import_categories'
         1: from script/import_scripts/vbulletin.rb:287:in `each'
script/import_scripts/vbulletin.rb:289:in `block in import_categories': undefined method `[]' for nil:NilClass (NoMethodError)
1 „Gefällt mir“

Wahrscheinlich. Ich habe seitdem eine ganze Menge vBulletin-Importe durchgeführt. :person_shrugging:

Du musst es einfach ausprobieren und sehen, was passiert. Es sieht genauso aus, wie ich es beschrieben habe.

Ich würde das Skript einfach so modifizieren, dass es . . . etwas tut . . ., wenn diese Sache nil ist.

1 „Gefällt mir“

Absolut, aber ich weiß nicht genug darüber, wie Discourse funktioniert, um zu wissen, was ich einstellen soll.
Was würde Discourse tun, wenn ich sie auf eine zufällige Zahl wie 0 setzen würde? Oder sollte ich eine Kategorie-Nummer finden, die bereits in der DB vorhanden ist, und sie darauf setzen?

Nicht so stark in Ruby, würde das deiner Meinung nach funktionieren?

        if categories.detect { |c| c["forumid"] == cc["parentid"] }["parentid"].nil?
          cc["parentid"] = 52
        else
          cc["parentid"] = categories.detect { |c| c["forumid"] == cc["parentid"] }["parentid"]
        end

Eigentlich scheint es, dass es viele gelöschte Foren gibt, deren parentid nicht mehr existiert.

EDIT
Ich habe sie gerade alle auf ein übergeordnetes Thema gesetzt, und ich kann es später reparieren.

1 „Gefällt mir“

Wir sind endlich beim Import von Anhängen angekommen, es war bei etwa 1,9% und jetzt erhalten wir diesen Fehler

    67406 / 3550728 (  1.9%)  Traceback (most recent call last):
        23: from script/import_scripts/vbulletin.rb:1006:in `<main>'
        22: from /var/www/discourse/script/import_scripts/base.rb:47:in `perform'
        21: from script/import_scripts/vbulletin.rb:88:in `execute'
        20: from script/import_scripts/vbulletin.rb:610:in `import_attachments'
        19: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/querying.rb:22:in `find_each'
        18: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/relation/batches.rb:70:in `find_each'
        17: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/relation/batches.rb:137:in `find_in_batches'
        16: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/relation/batches.rb:229:in `in_batches'
        15: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/relation/batches.rb:229:in `loop'
        14: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/relation/batches.rb:245:in `block in in_batches'
        13: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/relation/batches.rb:138:in `block in find_in_batches'
        12: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/relation/batches.rb:71:in `block in find_each'
        11: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/relation/batches.rb:71:in `each'
        10: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/relation/batches.rb:71:in `block (2 levels) in find_each'
         9: from script/import_scripts/vbulletin.rb:651:in `block in import_attachments'
         8: from script/import_scripts/vbulletin.rb:651:in `each'
         7: from script/import_scripts/vbulletin.rb:659:in `block (2 levels) in import_attachments'
         6: from /var/www/discourse/script/import_scripts/base.rb:873:in `html_for_upload'
         5: from /var/www/discourse/script/import_scripts/base/uploader.rb:40:in `html_for_upload'
         4: from /var/www/discourse/lib/upload_markdown.rb:10:in `to_markdown'
         3: from /var/www/discourse/lib/upload_markdown.rb:19:in `image_markdown'
         2: from /var/www/discourse/app/models/upload.rb:206:in `short_url'
         1: from /var/www/discourse/app/models/upload.rb:534:in `short_url_basename'
/var/www/discourse/app/models/upload.rb:270:in `base62_sha1': undefined method `hex' for nil:NilClass (NoMethodError)

undefined method `hex’ for nil:NilClass (NoMethodError)

Hat jemand eine Ahnung, wie man das beheben kann?

Versucht es, short_url_basename zu lesen, und es gibt nil zurück; also schlägt .hex fehl?

1 „Gefällt mir“

Meine Vermutung, ohne den Code zu sehen, ist, dass die Datei fehlt oder vielleicht gibt es ein Feld für den Dateinamen und es ist leer? Ich würde wahrscheinlich ein puts in import_attachments einfügen und sehen, was in dem Datensatz ist, den es zu importieren versucht.

1 „Gefällt mir“

Vielen Dank für die Hilfe! Ich bin neu in Ruby, wäre das der richtige Weg, um das zu tun?

      unless mapping[post.id].nil? || mapping[post.id].empty?
        mapping[post.id].each do |attachment_id|
          upload, filename = find_upload(post, attachment_id)
          unless upload
            fail_count += 1
            next
          end

          puts "{short_url_basename}"

          # interne Upload-Deduplizierung stellt sicher, dass wir Anhänge nicht erneut importieren
          html = html_for_upload(upload, filename)
          if !new_raw[html]
            new_raw += "\n\n#{html}\n\n"
          end
        end
      end

Aha, short_url_basename ist eine Funktion, das wird also nicht funktionieren.

Ist es einfach puts "{post}"? Und es wird den gesamten Inhalt des Post-Objekts ausgeben?

Dies scheint die Zeile zu sein, die in upload.rb abstürzt.

upload_markdown 19
"![#{@upload.original_filename}|#{@upload.width}x#{@upload.height}](#{@upload.short_url})"

upload.rb 534
"#{Upload.base62_sha1(sha1)}#{extension.present? ? ".#{extension}" : ""}"

upload.rb 270
Base62.encode(sha1.hex)

Es ist also entweder upload.original_filename, upload.width, upload.height oder upload.short_url.

Wenn ich also eine Nil-Prüfung in upload_markdown durchführe, sollte das den Fehler verhindern, oder?

Benötigt es die shortURL, damit es funktioniert? Könnte ich einfach meine eigene zufällige shortURL erstellen?

2 „Gefällt mir“

Ich glaube, da liegt das Problem. Es findet den Upload nicht und gibt daher nil zurück. Vielleicht fehlt die Datei oder sie ist ungültig.

1 „Gefällt mir“

Aber würde das es dann nicht abfangen?

unless upload
  fail_count += 1
  next
end

Oder prüft unless nicht auf nil?

Oder wird es bestanden, weil das Upload-Objekt erstellt wurde, aber die Eigenschaft upload.short_url im Upload-Objekt fehlt, vielleicht?

1 „Gefällt mir“

Tut. Richtig. Das würde es abfangen. Ich fürchte, deshalb ist dieses Maß an Fehlersuche für ein Forum nicht wirklich angemessen. :person_shrugging:

Sie sind jedoch auf dem richtigen Weg. Machen Sie einfach weiter. Es scheint, als wüssten Sie genug, um es herauszufinden. Ich habe mindestens ein paar Importeure geschrieben, bevor ich Ruby gelernt habe.

1 „Gefällt mir“