HN 日本語サマリー

← 一覧へ戻る
AI・機械学習

重要なメッセージバスのスケーリングとベンチマーク:新しいインデックス戦略の使用

Scaling and benchmarking a critical message bus using a new indexing strategy (blog.janestreet.com)

22 pointsby eatonphil0 コメント

要約

Jane Streetの内部メッセージングフレームワークAriaは、日量数テラバイトのデータを処理しています。最近、クライアントがメッセージのサブセットのみを読み取る際のコストを削減するため、新しいインデックス戦略が導入されました。この最適化により、CPU使用率が30%削減され、クリティカルなシステムに必要な高い正確性を維持しました。

全文翻訳

重要なメッセージバスのスケーリングとベンチマーク:新しいインデックス戦略の使用 2026年10月5日 | 10分読む Facebookで共有 Twitterで共有 LinkedInで共有 著者:Nicholas Yang 以下は、2026年夏のインターンプロジェクトに関する一連の記事の一部です。詳細については、「インターンがもたらしたもの、特別ジャンボ2026年版」を参照してください。 Ariaは、日量数テラバイトのデータを処理する当社の内部メッセージングフレームワークおよびホストシステムです。クライアントはAriaを購読して、メッセージのライブストリームを取得できます。 Ariaの使用量が当社で急速に増加するにつれて、データ量とスループットの増加に合わせてシステムをスケーリングするための最適化と再設計の機会を見つける必要がありました。 Ariaチームで働くインターンのTheodor Totevは、この夏、インデックス作成とツリー分割を使用して特定のユースケースに焦点を当てました。クライアントがメッセージのサブセットのみを読み取るのをより安価にするにはどうすればよいでしょうか?彼の最適化は、クリティカルなシステムに必要な非常に高い正確性を維持しながら、本番ワークロードで実行する際のCPU使用率を30%削減しました。 メッセージのフィルタリングがサーバーに負荷をかけていました AriaがTCP経由でクライアントにメッセージを配信する際、ストリームの最近の部分、つまり「ストリームの先端」をインメモリリングバッファに保持します。クライアントが遅延した場合、最新の状態に戻るために、このリングバッファから最近のメッセージを要求できます。このプロセスを「先端回復」と呼びます。 先端回復の1つの課題は、Ariaがストリーム全体を保存しているのに対し、クライアントはしばしばはるかに小さいサブセットのメッセージしか気にしないということです。Ariaはメッセージストリームをトピックに分割し、これらはファイルシステムのような階層的な名前空間を形成します。クライアントは個々のトピックまたはトピックサブツリー(グロブスターに似た、指定されたトピックの下にあるすべてのトピック)を購読できます。 Ariaは、ストリーム全体を購読しているトピックからのメッセージのみにフィルタリングし、これらのメッセージをクライアントに配信します。 当初、アルゴリズム的には非効率的ですが、ストリームをループすることはCPUキャッシュに優しいため、比較的速かったため、単純な線形パスとしてこのフィルタリングを行っても問題ありませんでした。しかし、先端回復を行うクライアントの数が増えるにつれて、サーバーが増加した負荷に苦しんでいることに気づきました。一部のサーバーはCPU使用率100%に達し、クライアントが「先端から落ちる」という結果になり、追いつけなくなりました。一時的な修正としてサーバーを追加しましたが、先端回復コードを再考する必要があることは明らかでした。 Theodorは、Ariaがストリームからメッセージを効率的にフィルタリングするために使用できるインデックスデータ構造を追加することで、この問題の解決を決定しました。しかし、何をインデックス化すべきでしょうか?ナイーブなアプローチは、各トピックにインデックスを作成することでしょう。しかし、Ariaインスタンスは100万近くのトピックを持つことができるため、このアプローチは実行不可能になります。代わりに、Theodorは各トピックパーティションにインデックスを作成しました。トピックパーティションは、同じ2つのセグメントプレフィックスを持つすべてのトピックで構成されます。セグメントとは、スラッシュで区切られたトピック名の単一の部分です。たとえば、app/codestore/commitsとapp/codestore/featuresは、どちらもapp/codestoreトピックパーティションの下にあります。 各トピックパーティションは、Ariaストリーム内のメッセージの位置のインデックスを取得します。 クライアントがメッセージを要求すると、Ariaは最小ヒープを使用してインデックスのn-wayマージを実行します。これにより、Ariaはこれらの特定のトピックパーティションのメッセージストリームを効率的に、順序通りに再構築できます。 インデックスのプロトタイピングとベンチマーク パフォーマンス作業には、実際にシステムを改善していることを検証するためにベンチマークが必要でした。メッセージ発行ロジックはAriaのクリティカルパスにあるため、広範なテストも望んでいました。Theodorは、さまざまな回復シナリオをプロファイルするツールを作成することから始めました。これにより、トピックパーティションの数、インターリーブの度合い、リーダーの数などのさまざまな構成をテストし、これらの変数を変更することがシステムパフォーマンスにどのように影響するかを確認できました。 ベンチマークができたので、トピックパーティションインデックスの初期バージョンを実装しました。この実装をプロファイルしたところ、最小ヒープがコードの最大のボトルネックであることがわかりました。AI以前であれば、おそらくより良いと思われる単一の実装を選択したでしょう。しかし、最近では、実験を実行するのは簡単です。Theodorはエージェントに5つの異なるヒープ実装を起動させ、一晩中プロファイルさせました。翌日、答えが得られました。fast_heap_unboxedは、インデックス付き回復コードのパフォーマンスを2倍に向上させました。 Theodorは、多数の期待テストで新しいインデックスをテストし、Antithesisで新しいロジックを実行し、最終的に多数のエージェントにコードを厳密に分析させました。 ブロックプールを使用してインデックスを保存する インデックスデータ構造を考案した後、効率的な表現方法を見つける必要がありました。リングバッファは動的にリサイズできないため、各インデックスにリングバッファを作成したくありませんでした。したがって、最悪のシナリオに対応するようにサイズを設定する必要があります。具体的には、インデックスサイズはメッセージサイズに反比例します(メッセージが小さいほど、チップストアに多くのメッセージを格納でき、より大きなインデックスが必要になります)。Ariaメッセージは32バイトという小ささになることがあります。チップストア全体がそのような小さいメッセージで構成されている場合、対応するインデックスは2GBになります。トピックパーティションごとに2GBのインデックスを保持すると、メモリを無駄に消費しすぎます。代わりに、インデックスがトピックパーティション内のメッセージ数に応じて動的に縮小および拡大するようにしたかったのです。 私たちが落ち着いたのは、各々1024エントリを含むブロックの共有プールを使用することでした。インデックスはブロックを参照し、そこに値を挿入します。ブロック内のすべてのメッセージがリングバッファから外れると、ブロックはインデックスからポップされ、他のインデックスで再利用できます。 古いメッセージの読み取りに時間がかかりすぎていました インデックス作成に関するTheodorの作業は、チップ回復の問題を解決しましたが、別のメッセージ配信の問題がありました。ストリームの先端よりも前のメッセージを必要とするクライアントは、待ち時間が長すぎました。クライアントが再起動すると、多くの場合、購読しているトピックについて、その週の初めからのすべてのメッセージを要求します。このプロセスを「初期回復」と呼びます。 初期回復は、数百万、場合によっては数十億のメッセージを含む可能性があります。Ariaがこれらのメッセージを読み取り、クライアントに可能な限り迅速に配信することが絶対に重要です。また、クライアントはほぼ同時に再起動する傾向があるため、このプロセスがうまくスケーリングすることが特に重要です。 ある特定のインシデントでは、通常2.5秒未満で完了する初期回復が13分以上かかりました。その理由を調査したところ、チップストアを悩ませていたのと同じ問題が見つかりました。Ariaは、実際に送信する必要があるデータの10倍のデータを読み取り、フィルタリングしていました。ディスクにメッセージをどのように保存するかを再考する必要があることは明らかでした。 Ariaはメッセージをサブツリーストアに永続化します Ariaは当初、各トピックパーティションのメッセージを時系列セグメントとしてディスクに保存します。その後、別のプロセスがこれらのセグメントを取得し、複数のサブツリーストアに分割します。これらのサブツリーストアには、トピックサブツリーのメッセージが含まれます。これらのサブツリーストアは、多数のファイルへの書き込み(効率的なフィルタリングが可能だが、メッセージストリームへの再結合に多くの作業が必要)と、少数のファイルへの書き込み(再結合は容易だが、フィルタリングは非効率的)のバランスを取ることを目的としています。 この分割を行うために、Ariaは単純なヒューリスティックを使用していました。トピックの最初の3つのセグメントを読み取ることでサブツリーストアを選択していました。たとえば、app/options/orders/createdとapp/options/orders/cancelledというトピックがあった場合、それらは両方ともapp/options/ordersのサブツリーストアに入れられます。トピックに3つ未満のセグメントがある場合、独自のサブツリーストアに入れられました。たとえば、app/optionsは独自のストアに入れられます。効果的に、このヒューリスティックは、トピックパーティションの各直接の子に個別のサブツリーストアを与えました。しかし、ストア内の1つのトピックが他のトピックよりも著しく多くのメッセージを持っている場合、このヒューリスティックはうまく機能しませんでした。クライアントが購読した場合、