8月27日、Cloudflareが「How we saved 100 terabytes of memory by optimizing 1.1.1.1's DNS cache」と題した記事を公開した。DNSキャッシュのデータ構造を5段階にわたって最適化し、サーバーフリート全体で約100TBのメモリを削減した技術的取り組みを詳細に解説している。
Cloudflareの1.1.1.1は世界有数のパブリックDNSリゾルバーだ。そのインフラを支える社内プラットフォーム「Big Pineapple」は、1.1.1.1のほかGateway DNS、DNS Firewallなど複数のDNSサービスを一手に担い、常時2,500億件以上のDNSキャッシュエントリを保持している。このスケールになると、1エントリあたり1バイトの無駄がフリート全体で250GB超のメモリ消費に直結する。「1バイトの重さ」が文字通り桁違いになる世界での最適化記録だ。
5段階の最適化で達成した成果
今回の取り組みによって、エントリあたりのメモリフットプリントは50%以上削減され、フリート全体で約100TBのメモリが解放された。これはCloudflareのGen 13サーバー130台分のRAMに相当する。パフォーマンス面でもインサートスループットが43%向上し、ルックアップレイテンシが19%低下するという副次効果も得られた。
最大の問題:Rustのenum肥大化
エンジニアが最も注目すべきポイントは、Rustのenum(直和型)のサイズ問題への対処だ。
DNSレコードのデータは種類ごとに異なるサイズを持つ。これをenumで表現すると、Rustの仕様上、全バリアントの中で最大のものに合わせてメモリが確保される。最大バリアントのNAPTRレコードは136バイトで、タグとパディングを含めるとenumのサイズは144バイトになる。一方、トラフィックの80%以上を占めるAレコードは4バイト、AAAAレコードは16バイトで足りる。つまりAレコード1件ごとに120バイト超のパディングが無駄になる。
pub enum RecordData {
A(Ipv4Addr), // 4バイト
Aaaa(Ipv6Addr), // 16バイト
Txt(Txt),
Naptr(Naptr), // 136バイト ← 全体サイズを支配する
Svcb(Svcb),
// ...
}
解法その1:大きなバリアントをBoxingする
最初に試みたのは、大きなバリアントだけをヒープに移し、enumにはポインタ(8バイト)だけを持たせる手法だ。A/AAAAレコードで1件あたり120バイト削減できる。ただしBoxingには「アロケーターのオーバーヘッド(jemallocのサイズクラス管理による切り上げ)」と「メモリローカリティの悪化によるCPUキャッシュミス増加」という2つのコストが伴うため、単純な解決策にはならなかった。
解法その2:レコードデータをrawバイト列として連続バッファに詰める(最大効果)
Boxingの欠点を解消するために採用したのが、レコードデータをrawバイト列として連続したBox<[u8]>に格納する手法だ。各レコードを「2バイトの長さプレフィックス+rawバイト」の形式で順に詰め込む。
これによりバリアントごとのenum肥大化が消え、個別ヒープアロケーションもなくなり、CPUキャッシュローカリティも改善する。DNSレスポンス構築時にA/AAAA/TXT/DNSSECレコードをバッファから直接コピーできるため、ルックアップパスの処理量も減少した。この変更だけでルックアップレイテンシが5%低下している。
トレードオフとして、レコードをランダムアクセスではなく順次スキャンする必要が生じるが、1エントリあたりのレコード数は少ないため実用上の問題はない。書き込み時は、キャッシュ挿入をまたいで再利用される「スクラッチスペース」バッファを介することで、Vec<u8>使用時に生じるアロケーター非効率を回避している。インサートスループットはこの変更だけで13%向上した。
その他の3つの最適化
Vec<T>をBox<[T]>に置き換える:Vecはポインタ・長さ・容量の3フィールドを持つが、キャッシュ格納後はサイズが変わらない。容量フィールド不要のBox<[T]>に置き換えることで64バイト/エントリを削減。この変更だけで15TB超の節約になった。
セクションを1リスト+オフセットで管理する:answer/authority/additionalを別々のリストで持つ代わりに、1つのリストとu16のオフセット2本で表現することで28バイト/エントリ削減。
レコードオーナーを省略する:多くのDNSレコードは所有者ドメインがクエリと同一のため、明示的に格納せずルックアップ時にキャッシュキーから復元する。CNAMEチェーンのような例外のみSome(Box<Name>)で格納する設計だ。
本番環境でのロールアウト
各最適化はコードパスへの影響が大きいため、2026年5月18日から段階的にロールアウトを開始し、2026年7月6日に全サービスへの展開が完了した。p90/p98/p99のいずれの水準でも、本番インスタンスのメモリ消費が段階的に低下したことが確認されている。ベンチマーク上の削減率と実測値の差はトラフィック特性やアロケーター状態に起因するが、フリート全体で100TBという削減量はその差を補って余りある規模だ。
詳細はHow we saved 100 terabytes of memory by optimizing 1.1.1.1's DNS cacheを参照していただきたい。