この記事は、サードパーティの古い情報源のみに依存しており、発明された Aurabase 暗号には決して依存しません。バックエンドに固有の p99 レイテンシは含まれません。私たちはガベージ コレクターを使用せずに、Rust でアプリケーション コアを作成しました。これは、コード、ワークスペース Cargo、および axum サービスで直接検証できる事実です。ただし、これを数値で実証するための再現可能な p99 ベンチマーク手法はまだ公開されていません。本文はメカニズムを説明するものであり、測定結果ではありません。
必需品
- p99 は、100 件中最も遅いクエリを測定します。これは、平均ではなく、ガベージ コレクター (GC) の一時停止が最も痛む場所です (Dean & Barroso、「The Tail at Scale」、Google、2013)。
- GC はプログラム全体を中断し (「ストップ・ザ・ワールド」)、未使用のメモリーを解放します。 Rust には GC がありません。コンパイラによって検証され、値がスコープ外になった正確な瞬間にメモリが解放されます。
- Discord は、2020 年に Go が少なくとも 2 分ごとにガベージ コレクション サイクルをトリガーし、各サイクルでレイテンシーの急増を引き起こすキャッシュ サービスについて文書化しました (Discord Engineering Blog)。
- GC の一時停止を減らすには、Google であっても何年ものエンジニアリングが必要です。GB コレクターは 2015 年から 2018 年にかけて 300 ~ 400 ミリ秒から 500 マイクロ秒になりましたが、ゼロになることはありませんでした (go.dev)。
- Aurabase のバックエンドコアは Rust で書かれており、ガベージコレクターは使用せず、コードで検証されています。 p99 Aurabase レイテンシの数値は現在まで公開されていません。これは測定ではなくメカニズムのままです。
p99 が平均ではない理由
平均は本質を隠します。 100 件のリクエストのうち 99 件が 5 ミリ秒以内に応答し、1 件だけが 500 ミリ秒かかる場合、平均は低いままです。しかし、100 人に 1 人のユーザーは、その 100 倍の待ち時間を経験します。 p99 はまさにこのリクエストを測定します。最も遅い 100 番目のリクエスト、つまり、平均レイテンシ ダッシュボードが緑色のままで SLA に違反しているリクエストです。
Google では、Jeffrey Dean と Luiz André Barroso が、「The Tail at Scale」 (Communications of the ACM, vol. 56, 2013) でこの問題を形式化しました。彼らの観察は、以来頻繁に引用されています。「中程度の規模のシステムでは重要ではない、一時的な高遅延エピソードが、大規模なシステムでは全体的なサービス パフォーマンスを支配するようになる可能性がある」。つまり、小規模では無視できる程度の遅延が時折発生し、最終的には分散システムの知覚されるパフォーマンスを支配してしまうのです。
1 秒あたり数千のリクエストを処理するバックエンドは、GC 一時停止中に発生したリクエストをどこかの時点で送信することになります。大規模な場合、これは珍しいケースではありません。これは統計的に確実です。
ガベージ コレクターの機能とすべてを一時停止する理由
ガベージ コレクター (GC) は、プログラムの生きているオブジェクト (まだどこかで参照されているオブジェクト) を継続的に追跡し、アクセスできなくなったオブジェクトのメモリを解放します。この追跡は tracingと呼ばれます。GC は参照グラフを調べ、まだ使用されているものをマークし、残りをスイープします。
問題は、プログラムが新しい参照を作成し続けている間にこのグラフを走査すると、一貫性のない結果が生成されるということです。歴史的な答えは、多くの最新の GC で今でも最後の手段として使用されている stop-the-world です。マークとスキャン中にプログラム全体が一時停止します。ヒープが大きくなるほど、一時停止は長くなる傾向があります。その期間は、現在のワークロードではなく、ライブ データのサイズによって決まります。
最新の GC のほとんどは世代戦略を採用しています。つまり、オブジェクトの大部分が早期に消滅すると想定しています。したがって、最近の割り当ては、小さなメモリ領域で頻繁に、しかし迅速にスキャンされます。数サイクルを乗り越えたオブジェクトは、スキャン頻度が低く、より大きな領域に移動しますが、その領域をクリーンアップする必要がある場合、そのサイズに応じて関連する一時停止が大きくなります。高トラフィック、高割り当てのサービスの p99 を支配するのは、小さな「マイナー」な中断ではなく、この「メジャーな」中断です。
最新の同時 GC と世代別 GC は、プログラムと並行して動作することで、こうした中断の頻度と期間を短縮します。しかし、ほとんどすべての企業は、境界線に達した場合に備えて、世界を阻止するフォールバック メカニズムを備えており、これを削減するには何年ものエンジニアリングが必要です。セクション 04 では、定量化され情報源に基づいた例が示されています。
Discord、2020: GC の中断が本番環境のインシデントになる
2020 年 2 月、エンジニアの Jesse Howarth が業界で参考になった投稿、「Discord が Go から Rust に切り替わる理由」 (Discord Engineering Blog) を公開しました。関連サービス Read States は、数百万のユーザー (キャッシュごとに数千万のエントリ) のメッセージ読み取りステータスを 1 秒あたり数十万の更新で管理します。
診断は直接的なもので、記事にそのまま引用されています:「Go は少なくとも 2 分ごとにガベージ コレクションの実行を強制します」。言い換えれば、Go はこのサービス上で少なくとも 2 分ごとにガベージ コレクション サイクルをトリガーし、各サイクルによってレイテンシのスパイクが発生し、チームのグラフに表示されます。
チームはまずキャッシュ サイズを削減して、スパイクを平準化しました。この妥協案は依然として不利なままでした。GC の一時停止は減りましたが、データベースに落ちるキャッシュ ミス リクエストが増加したため、他の場所では全体的な p99 が低下しました。基本的な修正は、監視するガベージ コレクターを使用せずに、Rust でサービスを書き直すことでした。
この投稿は活発な技術的な議論を引き起こしました。公開同日 (2020 年 2 月 4 日) に、Hacker News には 1,580 を超えるポイントと 642 件のコメントが付けられました。これは、問題が Discord のケースをはるかに超えていることを示しています。
Google での 3 年間のエンジニアリングにより、一時停止を 400 ミリ秒から 500 μ秒に短縮
Go ガベージ コレクターは、Google の専任チームのリソースを使用した場合でも、GC の一時停止を制御するために必要な労力の規模を示しています。 Go GC テクニカル リードの Rick Hudson は、このストーリーを 2 つの Go 公式ブログ投稿で文書化しました。
| 2015年8月以前 | 300~400ミリ秒 | 歴史的な囲碁コレクター、再設計前 |
|---|---|---|
| 2015 年 8 月 · Go 1.5 | 30~40ミリ秒 | 最初の競合コレクタ、ターゲット < 10 ms セット |
| 2016 · ゴー 1.6 | < 10 ms (SLO 保持) | 本番環境で当初の目標を達成 |
| 2017 年 3 月 · Go 1.8 | ミリ秒未満 | stop-the-world スタック スキャンの削除 |
| 2017 年 8 月 · Go 1.9 | 100~200μs(マーク) | チームが言及した新しい非公式ベンチマーク |
| 2018 · SLO が発表されました | 1サイクルあたり500μs | Rick Hudson によって策定されたサービス目標 |
出典: 「Getting to Go: The Journey of Go’s Garbage Collector」、go.dev、2018 年 7 月 12 日。および 「Go GC: 低レイテンシーとシンプルさを優先する」、go.dev、2015 年 8 月 31 日。
3 年間の献身的な取り組みにより、典型的な休憩時間は 1,000 分の 1 に減少しました。しかし、一時停止は解消されませんでした。これはサービス目標 (SLO) であり、ゼロを絶対に保証するものではありません。 GC トレースは、その構造上、生きたオブジェクトのグラフを時々横断する必要があります。唯一の調整可能な変数は、この旅の頻度と期間であり、その存在ではありません。
この優先順位の選択は中立ではありません。 Go は主にネットワーク サービスと Web バックエンドをターゲットにしており、数百ミリ秒の停止がユーザー エクスペリエンスに直接影響を与えるため、生の GC スループットではなくレイテンシに多大な労力が費やされます。他のマネージド ランタイムは、独自の低一時停止コレクターで地位を確立する前に、歴史的なユースケースによって形成されたさまざまなトレードオフを継承しました。共通点は同じです。それらはすべて GC トレースから開始されるため、一時停止メカニズムは最小限に抑えられ、構築によって削除されることはありません。
なぜRustは構造的にこの問題を抱えていないのか
Rust は GC 一時停止を減らすのではなく、GC 一時停止を引き起こすメカニズムを排除します。コンパイラは、コンパイル時に、各メモリ値の所有者 (ownership) を追跡します。値の所有者がスコープ外になると、Rust はそのメモリを解放する呼び出しをバイナリ コード内の同じ場所に自動的に挿入します。このメカニズムは RAII (リソース取得は初期化) と呼ばれます。リリースは決定的であり、バックグラウンドで実行されているガベージ コレクターによってスケジュールされません。
Scala/Rust エコシステムで認知されている技術ブログの著者 Alexandru Nedelcu 氏は、最近の記事「Rust のトレードオフは、予測可能なレイテンシと安全性によるパフォーマンスを優先し、使いやすさの 1 つです。」 (alexn.org、2026 年 7 月 21 日) でトレードオフを要約しています。 Rust では、記述の単純さの一部と引き換えに、予測可能なレイテンシーが得られます。
同じ記事では、最新の GC が常に十分ではない理由を要約しています。「最新の GC は、プログラムに影響を与えることなく、作業を段階的に並行して実行しようとします。しかし、その能力には限界があり、プログラム全体をフリーズさせるストップ・ザ・ワールド GC サイクルに後戻りしてしまい、レイテンシーに影響を及ぼします。」
以下にメカニズムを約 10 行で示します。これは一般的な例であり、Aurabase コードからの抜粋ではありません。
重要なニュアンス: すべてが無料というわけではありません。参照カウント タイプ (Rc、 Arc) では、各クローンとリリースにわずかなコストが追加されます。このコストは局所的であり、決定的です。プログラムがメモリ ヒープを通過する間に、プログラム全体がフリーズするような一時停止は発生しません。
非同期バックエンドに関する有益な説明: Rust 非同期ランタイム (tokio、すべての Aurabase サービスで使用される) はガベージ コレクターとは何の関係もありません。スレッドのプールで協調タスクをスケジュールしますが、メモリを解放するためにライブ オブジェクト グラフを反復処理することはありません。非同期ランタイムと GC が同じ仮想マシンによって管理されるエコシステムでは、混乱がよく発生します。
高トラフィックのバックエンドで何が変わるか
数千の同時リクエストを処理するバックエンドでは、GC が存在しないため、方程式 p99 から変数が削除されます。メモリ ヒープのサイズを調整したり、コレクターの世代を調整したり、最悪のタイミングで発生する可能性のあるサイクルを監視したりする必要はもうありません。個々のリクエストのレイテンシーは、プログラム内の他の場所で発生する予測不可能なグローバル イベントではなく、リクエスト自体の作業に依存します。
Aurabase のバックエンドコアはこの原則を適用します。すべてのサービス (aura-gateway、 aura-auth、 aura-db、 aura-realtime、 aura-storage、 aura-functions、 aura-ai…) は Rust で書かれ、単一の Cargo ワークスペースに編成されます。これはリポジトリで直接確認できます。
Cargo.toml、ワークスペース エディション 2021、リゾルバー v2 からの実際の抽出 - Aurabase リポジトリで検証済み。
この事実は、現段階では証明されていません: Aurabase で測定された p99 レイテンシーの数値。私たちは、独自のバックエンド用の再現可能なベンチマーク手法をまだ公開していません。これは進行中の作業であり、今日入手できる結果ではありません。ガベージ コレクターが存在しないことは、コード内で検証されたメカニズムです。これだけでは、測定された p99 潜伏期の証拠にはなりません。私たちの議論も含め、この主題に関するマーケティング上の議論に直面する際には、この区別を念頭に置いてください。アーキテクチャの詳細については、の Aurabase と Supabase の技術比較 を参照してください。
p99 を正しく測定するには、代表的な負荷条件、十分に広いスライディング ウィンドウで計算されたパーセンタイル、本番環境に近いテスト環境など、独自の規律が必要です。この方法論を使わずに数字を発表することは、マーケティングの数字を発表するようなものです。これはまさにこの記事で拒否していることです。
GC がないことで解決できないこと
ガベージ コレクターを削除すると、テール レイテンシーの原因が 1 つだけ解消されます。すべてが解消されるわけではありません。 Rust バックエンドでは、ネットワークの待機、Postgres 接続プールの飽和、データベース ロックの競合、インデックス付けが不十分な SQL クエリ、サードパーティ API 呼び出しの遅さなどが原因で、依然として劣化した p99 が表示されることがあります。この記事で説明するメカニズムは、構造的な原因を取り除きます。他者に対する免疫を提供するものではありません。
たとえば、Aurabase では、各サービスは接続プール (sqlx) を介して Postgres と通信し、 NATS JetStreamを介して他のサービスと通信します。小さすぎるプール、消費に時間がかかる NATS サブスクリプション、または適切なインデックスのない SQL クエリは、ガベージ コレクターの不在に関係なく、それぞれ独自のレイテンシのスパイクを生成します。
実際的な結論: GC がないことは、p99 対応システムに Rust バックエンドを選択する適切なアーキテクチャ上の理由です。これ自体は、Aurabase においても他の場所においても、レイテンシーを保証するものではありません。重要な方法は変わりません。測定し、方法論を公開し、測定によって明らかになったものを修正します。 GC を使用してバックエンドから移行する場合は、Supabase から Aurabase への移行ガイド で、何が変更され、何が変更されないのかが詳しく説明されています。