Import スクリプトのメモリ使用量が高い

既存のボードからのインポートテストを試みています。通常投稿と非公開メッセージを合わせて約2500万件の投稿をインポートするため、処理を高速化するためにインポートスクリプトを複数コピーし、同時に実行してトピックの負荷を分散させています。この方法は数日間問題なく動作しましたが、時間の経過とともに各プロセスのメモリ使用量が徐々に増加し、それぞれ約2GBに達するようになりました。その後、サーバーはメモリ不足に陥り、約1600万件の投稿の時点でMySQLソースデータベースを強制終了させました。

システムメモリを24GBから32GBに増設しましたが、それでもインポートプロセスを1つ再起動して中断箇所から再開しようとすると、投稿のインポートを開始する前に、そのプロセスがすぐに約10GBのメモリを消費してしまいます。以前は8つのインポートプロセスを同時に実行できましたが、現在はメモリプールを拡大しても2つしか実行できません。なぜ、クリーンな状態でのインポートと、失敗後に再開する場合とで、メモリ使用量にこれほど大きな差が生じるのでしょうか?インポートプロセスを再度高速化するために、メモリフットプリントを削減する方法はありますか?128GB〜256GBのメモリを搭載したサーバーは費用対効果が非常に低く(インポート後は不要になるため)、2つのインポートプロセスのみで実行すると完了までに数週間を要することになります。

正規表現がループに陥っているか、それに似た状況のようです。デバッグメッセージを出力し、インポートで問題のある行をスキップしてください。

メモリ使用量は、インポートスクリプトの起動時に「既存の投稿の読み込み中…」(またはトピック)のセクションで発生しているように見えますが、実際の投稿処理中ではありません。私の確認では、このセクションはデータベースから投稿やトピックの情報を取得しているだけで、正規表現は使用されていないはずです。

@posts@topics 変数は、「topic_lookup_from_imported_post_id」メソッドのような処理に使われているようです。これは理にかなっていますが、スクリプトが初期実行だった当時は、現在見ているほどメモリ使用量は高くなかったのに、それらのメソッドは依然として正常に動作しています。

一括インポートスクリプトを確認しましたか?メモリ使用量が少なくなる可能性があります。

ただし、インポートスクリプトは古いユーザー、トピック、投稿のIDを新しいIDにマッピングするマップをメモリ上に保持するため、特に複数のコピーを保持する場合は多くのRAMを消費します。

最初のインポートを実行した後、新しいデータのみをインポートするために再度実行し、それがはるかに高速に動作することはおわかりいただいていますか?つまり、最初のインポートのために1ヶ月待った後でも、最終的なインポートはそれほど時間がかかりません。

一括インポートスクリプトの存在は認識していません。import_scripts ディレクトリ内に何かあるのでしょうか?

はい、ここで問題に直面しています。システムがメモリ不足になるまで、8 つのインポートプロセスを並行して実行していた初期のインポート中は、比較的順調に進んでいました。しかし、インポートプロセスを再起動して中断した箇所から再開しようとすると、各プロセスが初回クラッシュ時の約 5 倍のメモリを使用するようになってしまいました。

本番での移行時期の期待値を設定し、適切なテストを行うためには、完全なインポートを完了させる必要があります。現状では、パフォーマンスなどについて何に期待すべきか明確に把握できていません。また、投稿数が 1600 万件に達した時点で、データベースのサイズがすでに現在のデータベースの 50% 以上大きくなっていることにも気づきました。これは少し驚きです。インポートに長い時間がかかるため、不可能というわけではありませんが、期待値を「週単位」ではなく「日単位」で設定できれば、はるかに便利になるでしょう。

discourse/script/bulk_import at main · discourse/discourse · GitHub をご覧ください。

トピックと投稿については、トピックとその前のすべての投稿がすでにインポートされていない限り、そのトピック内の投稿をインポートできないため、並列インポートを実行するのは現実的ではありません。ユーザーとトピックについては並列プロセスを設けることも可能かもしれませんが、投稿については、トピックのすべての投稿を取得するようにスクリプトを書き換えない限り、並列インポートはできません。これは確かに可能ですが、私が使用したどのスクリプトもそのような動作はしていません。しかし、古い ID から新しい ID へのマップを RAM に保持するという問題が残ります。

2500 万件の投稿をインポートするのは手間がかかりますね。:slight_smile:

私が現在行っているのは、トピックを分割して、各トピックを単一のプロセスで処理するようにしています。完全な分割ではありませんが、単一の逐次プロセスに比べて数倍高速です。

ここが最も混乱する部分です。これは処理中に動的に行われているものだと推測されます。そのため、3日間にわたって各プロセスのメモリ使用量が増加するのを目撃しました。データベース接続が切れる直前には、各プロセスで2〜2.5GBのメモリを消費していました。

各プロセスは、自身がインポートした投稿に関するマッピングのみを維持しているのでしょうか?もしそうなら、インポートを再起動した後にメモリ使用量が急増した理由が納得できます。

その通りだと思います。他のプロセスは、他のプロセスによってインポートされたデータを持っていないため、正しく動作しないでしょう。あなたが行っている方法は機能しないと思います。

バッチインポートスクリプトを確認するか、base.rb を書き換えて、インポート ID へのリンクを別の方法で追跡する必要があります。

コードのデバッグに数週間費やす可能性は、単に待つよりも高いでしょう。単一プロセスの CPU 速度を向上させることが、処理を高速化する最良の方法です。

今のところ、このアプローチに問題は見当たりませんが、投稿インポート後に実行される一部のメソッドで問題が発生する可能性はあります。そのため、その時点で処理を一旦停止し、残りの処理を単一スレッドで実行して、確実にクリーンに処理できるようにしたいと考えています。

とはいえ、インポート ID リンクを別様に扱うというあなたの提案は、いずれにせよ良い案だと思われます。スクリプト実行中に、変数に任意の量のデータを保持し続けるのは、効率的ではありません。