Cloudflareは、Big PineappleプラットフォームのDNSストレージに対して行った一連の低レベル最適化の結果を発表した。このプラットフォームは、1.1.1.1、Gateway DNS、DNS Firewall、AS112、およびその他多数のDNSサービスを稼働させている。プラットフォームは常時2,500億件を超えるDNSエントリーを保存しているため、エントリーあたり1バイトを削減するだけで、フリート全体で250ギガバイトを超えるメモリを節約できる。
Rustで記述されたデータ構造に5つの変更を順次加えた結果、1エントリーのサイズは953バイトから420バイトへ、56%削減された。変更を全面展開した後、Cloudflareのフリート全体で使用されるワーキングセットメモリは約100テラバイト減少した一方、性能は犠牲になるどころか向上した。エントリー挿入速度は43%向上し、ストレージの検索時間は19%短縮された。
なぜDNSエントリーのサイズが重要だったのか?
Big Pineappleは起動時に空のストレージから開始し、クエリの到着に伴って満たされ、上限に達すると古いエントリーや使用頻度の低いエントリーが削除される。ストレージのサイズはデータセンターによって異なる。また、EDNS Client Subnetを使用すると、同じクエリに対して複数の応答を保存することがある。これは、authoritativeサーバーがクライアントのネットワークに応じて異なる応答を返す場合があるためだ。
各エントリーは、ドメイン名、レコードタイプ、一部の属性を特定するキーと、DNS応答、authorityセクション、additionalセクション、作成時刻、使用回数カウンター、TTLなどのメタデータを含む値で構成される。この規模では、余分なフィールドや予約済み領域は小さな内部実装の詳細にとどまらず、極めて大きな運用コストへと変わる。
どこで節約できたのか?
Cloudflareは、拡張可能なVecおよびString構造を、応答の保存後にデータが変更されないことから、固定サイズのBox<[T]>およびBox<str>構造に置き換えた。この変更により、拡張可能な構造が保持していた容量フィールドがなくなり、未使用の予約領域も抑えられた。各エントリーにはこの種のフィールドが8つ含まれているため、節約量はエントリーあたり64バイト、ストレージ全体では15テラバイトを超えた。
さらに、answer、authority、additionalの各セクションのリストを1つのリストにまとめ、より大きなポインターや長さの代わりにu16型のオフセットを使用した。これにより、エントリーあたり28バイトを節約できた。また、複数の論理フィールドを1つのbitflagに詰め込むことで、Rustが構造体内部に要求するメモリアライメントによる無駄な領域も削減した。
ほとんどのDNSレコードでは、レコードの所有者がクエリ対象のドメインと一致する。そのためCloudflareは、こうした場合に所有者名全体を保存せず、応答を構築する際にストレージキーから復元するようにした。CNAMEレコードで発生するように名前が異なる場合は、完全な名前を保存する。これにより、実際に必要なケースを維持しながら、ほとんどの所有者名についてメモリ割り当てをなくした。
サイズの大きいレコードタイプのコスト削減
RecordData構造体は、最大のレコードタイプと同じサイズのenumを使用していた。タグとアライメントを含めると、その最大タイプは144バイトのNAPTRだった。その結果、わずか4バイトしか必要としないAレコードや16バイトを必要とするAAAAレコードも、必要量を大幅に上回る領域を使用していた。AとAAAAはテストトラフィックの80%超を占めていたにもかかわらずである。
大きなタイプを別のBoxに入れる方法も試された。これはAおよびAAAAレコードの無駄を削減したが、個別の割り当てとメモリのlocalityに関する問題を追加した。最終的な解決策は、レコードデータ自体を、各レコードに2バイトの長さプレフィックスを付けたBox<[u8]>内の連続した生バイトとして保存することだった。これによりenumのコストと複数の割り当てがなくなり、プロセッサによるキャッシュメモリの利用効率も向上した。
この選択により、レコードはランダムアクセスできなくなり、順番に走査する必要が生じる。Cloudflareは、1エントリーあたりのレコード数が少ないためコストは限定的だと考えている。A、AAAA、TXT、DNSSECレコードを含むほとんどのタイプは、外向きのDNSメッセージに直接コピーできる。一方、CNAME、NS、MX、SOAのようにドメイン名を含むタイプでは、DNS名圧縮を適用するために解析が引き続き必要になる。
実際に何が変わったのか?
本番環境での測定では、常駐メモリが99パーセンタイルで9.3ギガバイトから5.3ギガバイトへ、43%減少した。また、90パーセンタイルでは6.5ギガバイトから3.8ギガバイトへ、42%減少した。エントリーあたりの割り当て量は1.1キロバイトから461バイトへ減少し、挿入速度は毎秒625,000エントリーから893,000エントリーへ向上した。検索時間は828ナノ秒から670ナノ秒へ短縮された。
変更の展開は2026年5月18日に開始され、2026年7月6日にすべてのサービスで完了した。Cloudflareは、本番環境の常駐メモリにはストレージ以外のデータも含まれるため、プロセス全体での実際の減少率は、エントリー単位で分離して測定した結果を下回ったと説明している。また同社は、解放されたメモリをストレージ容量の増加に再投資する計画だ。メモリ消費量を増やすことなく容量を拡大し、キャッシュヒット率を改善して上位サーバーへ送信されるクエリを減らすことを目指している。
この事例の重要性は、サービスが数千億件の要素を扱う場合、不要な容量の削除、割り当ての統合、localityの改善といったデータ構造の最適化が、ハードウェアリソースを直接追加するよりも大きな効果を生み得ることを示している点にある。ただし、いくつかのトレードオフは残る。生データによる保存はレコード処理を複雑にし、所有者名を再構築するにはストレージキーからの復元が必要になる。また、テスト結果は特定のトラフィック構成に基づいており、本番環境と完全には一致しない。そのため、このような最適化を評価する際には、理論上の数値だけでなく、本番環境での測定結果が依然として最も重要な基準となる。