vBulletin 5データベースの移行 - インポートスクリプトのエラー

はい、ユーザーのインポートだけでも完了できればそうするつもりです。現在、メールの作業を試みるとエラーが発生しています。

script/bulk_import/vbulletin5.rb スクリプトの最初の行を削除してください
# frozen_string_literal: true

「いいね!」 2

最初の3つの関数のみを実行した場合:

 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

結果:

整合性を確保するというメッセージは、完全なインポートが完了したときに表示されるものだと仮定していますか?それとも、各「ステップ」を実行するたびに実行し、ホストからdiscourseディレクトリのコピーをバックアップとして取得する必要がありますか?

「いいね!」 1

再度、次の4つの関数をアクティブにして起動すると、既存のIDのエラーが返されます。

これは「オール・オア・ナッシング」にできますか?おそらく、すべてを1つの大きなトランザクションで実行することを期待していますか?

もう一度試しました。プロセスはしばらく実行されていましたが、突然これが発生しました。

イライラするのは、まったく予期せず発生したように見えることです。
今、もう一度起動すると、このエラーが発生します。

もう疲れたので、何を参照しているのか確認できません。特に、バルクインポートスクリプトを単に再起動しただけであれば、duplicate key value はまったく発生しないはずですよね?

この投稿で攻撃されたと感じた方がいたら、まず謝罪させてください。正直なところ、月曜日からこれらの問題に取り組んでおり、これ以上Discourseのコードのデバッグやホットフィックスを行う気力はありません。

7回数えた後、7回目の試行で諦めることにしました。なぜなら、Discourseは移行をあまりサポートしていないように見えるからです。

最大の СHARSET 問題は、この巨大なデータベースで使用されている СHARSET が utf8mb4 であり、スクリプトがサポートしていないことだと考えています。

utf8(デフォルト)を使用すると、多くのエラーが報告されますが、スクリプトはそのまま続行するため、何が起こっているのか不明です。データベースのエントリはスキップされていますか?サポートされていない文字(古典的な四角形)でコピーされていますか?

さらに、最新の3回の実行(バルクインポータを使用)では、まったく同じ指示に従ったにもかかわらず、結果が異なります。最後の実行では、トピックのインポートに到達し、すぐにエラーを報告し始めましたが、続行しました(???):

アプリケーションをロード中...
開始中...
I18n をプリロード中...
最高の投稿番号を修正中...
インポートされたグループID をロード中...
インポートされたユーザーID をロード中...
インポートされたカテゴリID をロード中...
インポートされたトピックID をロード中...
インポートされた投稿ID をロード中...
グループインデックスをロード中...
ユーザーインデックスをロード中...
カテゴリインデックスをロード中...
トピックインデックスをロード中...
投稿インデックスをロード中...
投稿アクションインデックスをロード中...
カテゴリをインポート中...
親カテゴリをインポート中...
      5 -   1104/sec
子カテゴリをインポート中...
    500 -   1539/secエラー:  一意制約違反の重複キー値 \"unique_index_categories_on_name\"
詳細:  キー (COALESCE(parent_category_id, '-1'::integer), name)=(-1, Armata Brancaleone) は既に存在します。
コンテキスト: 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>'
トピックをインポート中...
    600 -   4073/sec
エラー: 未定義のメソッド `[]' 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>'

最終的にこれにクラッシュしました:

script/bulk_import/vbulletin5.rb:779:in `<main>'
 572329 -    531/sec
返信をインポート中...
client_loop: send disconnect: Connection reset

しかし、それ以前に、これら2つのエラーを絶えずスパムしていました:

エラー: 未定義のメソッド `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>'

および

エラー: 無効なバイトシーケンス 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>'

ステップバイステップで、実行する関数をコメントアウトし、rake import:ensure_consistency を実行してから、実行した関数をコメントアウトして続行しました。なぜなら、スクリプト全体を実行すると、以前実行したステップを再度実行してしまい、重複したIDが見つかってクラッシュするからです。

通常の「フリーソフトウェアなので文句は言えない」という議論が出る前に、他のオープンソースプロジェクトにも貢献しており、同様に無料でソフトウェアを作成していることを明確にしておきたいです。しかし、リリースしたものは機能し、十分に文書化されていることが私にとって非常に重要です(たとえ、それが「これはどのように機能するのか」という何千ものメッセージを回避するためだけでも)または、発生したバグを修正する準備ができています。

Discourseは優れたすぐに使えるエクスペリエンスを提供しているように見えますが、2022年であり、コミュニティはこの製品よりもずっと前から存在していたことは明確にしておくべきです。「採用」には強力な移行サポートが必要であり、Discourseの現在の状態ではないようです。

20GBのデータベースはエッジケースであると認識していますが、ここではサイズの問題ではなく、СHARSET またはその他の何らかの問題が発生しています。というのも、一貫したエラーがなく、ほとんどの場合、ドキュメントはなく、過去に同じ苦境を経験した人々が残したスレッドや投稿を検索し、回避策が見つかったことを願い、ソースコードがそれ以来あまり変更されていないことを期待するだけです。

現時点では、vbulletinからの移行を検討しているすべての人に、移行スクリプトのオーバーホール(進行中であるように見えますか?)が完了するまで、移行を保留することを強くお勧めします。

お気持ちはわかります(移行は非常に困難なテーマです)が、Discourseの移行スペシャリストとして、事実を申し上げます。

当社には成熟した移行フレームワークがあり、さまざまなプラットフォームに対応する60以上のスクリプトと、5つのスクリプトを備えた別のバルクインポートフレームワークがあります。さらに、パフォーマンス、コード構成、テスト容易性、検証可能性、ドキュメントなど、あらゆる側面を大幅に改善する新しいフレームワークも開発中です。

当社には、広範なコア開発者のサポートを受けた独立した移行チームがあり、完了した各移行でコードに一般的な改善を加えています。当社は、些細なものから信じられないほど複雑なものまで、顧客の移行を絶えず行っています。

当社の最終目標は、ホストされた顧客とコミュニティの両方にとって移行を可能な限り効率化することですが、移行の対象となるコードの量は膨大であり、システムレベルのソフトウェア構成、サードパーティ製ソフトウェアの変更、および入力データのばらつきが問題をさらに複雑にします。

重ねて申し上げますが、これらの作業がもっと楽になればと願っていますが、それには計り知れない労働時間が必要であり、利用できるリソースは限られています。

諦めないでください! :slight_smile:

「いいね!」 5

スコープが計り知れないほど大きいことは理解しており、感謝しています。例外の上に例外が積み重なる状況にフラストレーションを感じています。また、このプロジェクトがRubyで書かれていることも、ここに来る以外では助けを見つけにくい一因となっています。ご指摘の通り、実際のデータを持たずに対応できるはずのない、非常にニッチなケースもあるため、ここがすべてのヘルプ要求に対応することは不可能です。

また、vBulletinの構造が完全に混乱していることにも、かなりの部分で責任があると考えています。

今朝確認したところ、テーブルサイズの概要は以下の通りです。

文脈として、「text」テーブルには実際のコンテンツが格納されています。
node テーブルは階層を保持し、「closure」は…引用させてください。なぜなら、私自身も理解できないからです。

Closureテーブルは、すべてのノード間の親子関係を構築します。データベースの大部分は添付ファイルで構成されていますが、これはそもそもデータベースに保存されるべきではありません。

したがって、全体として、約8GBのコンテンツを持つフォーラムに対して、28GBのオーバーヘッドがあるということです。素晴らしいですね、vBulletin、おめでとうございます。

「いいね!」 2

これがフラストレーションを感じるということです。

もう一度、同じ手順(自分で書いた試行錯誤のランブックに従う)を、新しいDiscourseのインストールで実行します。

結果:

Tired Tv Land GIF by TV Land Classic

import_user_account_id はどこですか? :expressionless:
そして最も重要なことですが、トピックのインポートで失敗した前回の実行でエラーを引き起こさなかったのはどうやってですか? :confounded:

その関数呼び出し(どうやら重要だったようです)をコメントアウトして、再度実行します。

これらの重複キーのエラー…スクリプトは、すでにそれらのIDを処理したことを認識して、次に進むべきではないでしょうか?

インポートはそれぞれ異なります。your-previously-favorite-forumのインスタンスで動作するスクリプトは、そのまま動作するはずだと考えるでしょうが、そうではありません。そして、大規模なフォーラムの場合、それは本当に困難です。サポートするのは簡単なことではありません。バルクインポータは、レールが自動的にチェックできるようにするのではなく、データベースに直接アクセスします。

それはよくある問題であり、スクリプトのせいではありません。古いデータベースをutf8に移行する方法を見つける必要があります。

強力な移行サポートがあります。無料の移行サポートはありません。私は100回程度の移行を行い、サポートされていない、またはカスタムシステム用のインポートスクリプトをいくつか作成しました。データベースをインポートするには、おそらく3000〜5000ドルを請求します。これはオファーではなく、何度も経験した人にとってどれだけの仕事であるかを示すためのものです。Businessホスティングを1年間購入すれば、CDCKが無料で対応してくれる可能性があり、それは私が請求するよりも安いかもしれません。(ただし、そのサイズのデータベースではBusinessホスティングの対象とならない可能性があります)。

「いいね!」 1

さらに探索を続けます。

  • スクリプトが、存在しない関数 import_user_account_id を参照しています。開発者の皆さん(Discourse開発者)は修正を検討した方が良いでしょう。
  • トピックのタイトルをチェックするロジックが、何らかの理由でタイトルが空文字列になっているトピックに対して、なぜかエラーを起こしています。そのようなことは起こらないはずですが、それを評価するチェックはそれを捕捉して nil を返すはずですが、どうやらインポートに記述されている後続のロジックが壊れてしまっています(こちらを参照)。

最近インポートしたもので同様の問題が発生しました。トピックID XXX にはタイトルがありません」と返すか、投稿の最初の行のテキストをプルする方が良いですが、このコンテキストでは実行が困難です。私がやりたいことは、データベースを改変して、タイトルが欠落しているものに別の方法でタイトルを生成することだと思います。

はい、これらの問題が見つかったので、基本的にデータベースを「サニタイズ」していますが、スクリプトをデバッグして、毎回何が問題を引き起こしているのかを推測しなければならないので、大変です:sweat_smile:

まだ import_user_account_id という関数が見つからないことについては、手がかりがありません:expressionless:

特にホリデーがもうすぐそこまで来ていることを考えると、誰かが自分でそのスクリプトを使わない限り、修正される可能性は低いでしょう。(普段ならリチャードが助けに来てくれるのですが。)

「いいね!」 2

笑、今日はあなたを失望させることになりそうです。試しましたが、このインポーターのコミットには不完全な部分があり、base.rb の一部の変更が含まれていなかった可能性があります。@justin がこの件に取り組みましたが、彼なら知っているかもしれません。これは顧客固有の問題で、それ以上の影響なしにコメントアウトできる可能性があると疑っています。

私もバルクインポーターを使ったことはありません。

はい、インポートスクリプトは複雑で、データベース固有のものに依存することがありますが、一部のスクリプトは単純に動作しない状態です。このスクリプトも同様で、例えば # frozen_string_literal: true が付いているスクリプトの中には、すぐに動作しないものもあります。

「いいね!」 2

ハハ!

それは(少なくとも)

私が作成した変更をPRとして提出するのが非常に難しいと感じる理由の一部です。私が作業を終える頃には、そこには非常に多くのケース固有のものが含まれており、提出したものが何らかの方法で壊れてしまうのではないかと心配になります。

ええ。何かが実行されて、すべてのファイルに frozen_string_literal が追加されたのだと思います。ほとんどのファイルはテストがあったため修正されましたが、インポートスクリプトにはテストがありません。

「いいね!」 2

念のため、今すぐ誰かに修正してほしいわけではありません(カレン風に)。コードベース自体に明らかに問題がある点を指摘しているだけで、おそらく「おっと、コミットにこの変更を追加するのを忘れてた!」という類のものだと思います。

現時点では、この移行が1月より前に終わることはないだろうとすでに受け入れています。

皆さんはホリデーを楽しんでください :slight_smile:
ホリデー後に、この移行に費やす時間は間違いなく減りますが、この件について再度提起するか、新しいスレッドを開きます :frowning:

「いいね!」 2

そうですね、まったくその通りです。インポートに関しては、通常、異なるクライアントから2回インポートを実行してからでないと、PRを提出しません。

「いいね!」 1

これを最新の状態に保ちます。

コミュニティの他のエンジニアと話し合った後、base.rb にいくつかの変更を加えました。トピックのタイトルが '' になっていたため、gsub! がエラーを引き起こし、多くのエラーが発生していました。

コンテンツがない場合に、スレッドの imported_id を文字列として返す、normalize_text 関数を模倣した関数を追加しました。

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

次に、vbulletin5.rbcreate_topic の行を次のように変更しました。

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

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

これにより問題は解消されました。基本的に gsub!nil を入力として受け付けるのにあまり適していません。

しかし、これによりスクリプトは続行されましたが、import_private_topics に到達したときにハングしました。私たちのデータベースには 253,427 件のプライベートトピック(pm)がありますが、これは返信の数桁下です。9 時間後、何が起こっているのかを確認するためにスクリプトを停止しました。

インターフェースを起動すると、いくつかのことに気づきました。

  1. 管理ユーザーが作成されたときに同じメールアドレスを使用していたため、私のアカウントはインポートされませんでした。明白ですが、どこかに記載しておくべきことかもしれませんか?
  2. カテゴリ(vbulletin のサブフォーラム)の一部しかインポートされませんでした。
  3. トピックとその最初の返信のみがインポートされました(すべてかどうかは定かではありません)。また、それらはすべて、正しいカテゴリに入っていませんでした。カテゴリが作成されるはずだったものさえもです。すべてが「カテゴリなし」でインポートされています。
  4. 「返信カウンター」は -1 を示しています。おそらく返信はまったくインポートされなかったためでしょう。

全体として、この一括インポートには多くの問題がありましたが、ページネーションアプローチを実装していれば、これらの問題の多くは解決したでしょう。スクリプトがすべてを一度に処理しようとしたため、返信が失われたのだと思いますが、7GB のデータでは不可能でした。正直なところ、一括インポーターがページネーションアプローチでインポートを処理しないことに驚いています。たとえ 1000 件のレコードを一度に取得し、それらを書き込み、書き込まれた最後のレコード ID を保存してループするだけでも、大規模なデータベースの問題はすべて解決するはずです。

「いいね!」 1

参考までに、私はこの件を興味深く追っており、更新には非常に感謝しています。:pray: 私は移住についてはまだあまり知りませんが、これは非常に有益だと感じています。

「いいね!」 4