1.1.1.1 の DNS キャッシュが 56%スリムに!Cloudflare、100 テラバイトのメモリ削減に成功したよ
やっほー、しぃちゃんだよ! 今日は Cloudflare の Engineering ブログから、ちょっとマニアックだけどわくわくする話を持ってきたよ。なんと、DNS キャッシュのメモリ使用量を半分近くまで減らして、フリート全体で 100 テラバイトものメモリを解放しちゃったんだって!
Cloudflare Blogなにが発表されたの?
Cloudflare の Engineering ブログで、1.1.1.1 や Gateway DNS、DNS Firewall、AS112 といった複数の DNS サービスを支える基盤プラットフォーム「Big Pineapple」のキャッシュ最適化について紹介されていたよ。Big Pineapple は常時 2500 億件以上の DNS キャッシュエントリを保持しているんだけど、5 つの Rust レベルのメモリ最適化を積み重ねた結果、1 エントリあたりのメモリ使用量を 953 バイトから 420 バイトへと 56% も削減できたんだって。フリート全体では約 100 テラバイトのメモリが浮いて、これは Gen 13 サーバー 130 台分の RAM に相当する規模なの。
なぜ重要なの?
Big Pineapple が抱えるキャッシュエントリは 2500 億件以上。記事では「1 エントリあたり 1 バイト無駄にするだけで、フリート全体では 250 ギガバイト以上のメモリが失われる」と説明されていて、この規模だと数バイト単位の設計判断がそのままサーバー台数やコストに直結しちゃうの。DNS キャッシュはヒット率が高いほど上流への問い合わせが減って応答も速くなるから、メモリを切り詰めて浮いた分をキャッシュ容量に回せれば、それだけでサービス全体の効率が底上げされる構造になっているんだよね。
これで何が変わるの?
直接ユーザーが触れる機能ではないけど、効果はいろいろなところに波及するよ。
- キャッシュ挿入スループットが 1 秒あたり 625,000 件から 893,000 件へと 43% 向上
- キャッシュ検索のレイテンシが 828 ナノ秒から 670 ナノ秒へと 19% 短縮
- 本番環境でのメモリ使用量(p99)が 9.3 GB から 5.3 GB へと 43% 減少
そして何より、浮いた約 100 テラバイトのメモリはそのまま眠らせておくんじゃなくて、キャッシュ容量の拡大に再投資する方針なんだって。キャッシュ容量が増えればヒット率も上がるから、1.1.1.1 を使う私たちユーザーにとっても、応答速度や安定性という形で恩恵が返ってくるはずだよ。
深く潜ってみよう
ここからが今回の記事の本番、Rust の構造体レベルでの地道な削り込み技だよ。記事で紹介されている 5 つの最適化を順番に見ていくね。
Vec と String の容量フィールドを削除 Rust の
Vec<T>やStringは、成長のための予約領域を管理する「容量(capacity)」フィールドを持っていて、これだけで 8 バイトを消費するの。キャッシュエントリは一度作られたら伸び縮みしないから、この容量フィールドは無駄。Box<[T]>やBox<str>に置き換えることで予約領域そのものをなくし、1 エントリあたり 64 バイトを削減したよ。answer / authority / additional の 3 リストを 1 本に統合 DNS 応答は本来、回答・権威・追加情報という 3 つのセクションに分かれていて、それぞれ別の
Vecとして持つとポインタだけで 8 バイト×2 が必要になっちゃう。これを 1 本のリストにまとめて、各セクションの境界を 2 バイトのオフセット×2 で表すようにしたことで、1 エントリあたり 28 バイトを削減したの。所有者ドメイン名を「差分」だけ持つ レコードの所有者名(owner name)は、クエリしたドメイン名と同じであることがほとんど。そこで、クエリ名と一致する場合は
None、異なる場合だけSome(Box<Name>)としてヒープに確保する形にして、大半のレコードでヒープ割り当てそのものを不要にしたよ。大きな enum バリアントだけをボックス化 DNS レコードの種類ごとに構造体のサイズはバラバラで、NAPTR のような大きなレコード型が enum 全体のサイズを底上げしてた。サイズの大きいバリアントだけをヒープに逃がすことで、A / AAAA レコードのようなよくある小さいレコードで 120 バイトを削減できたの。
レコードをワイヤ形式のバイト列で保存 レコードを Rust の enum のまま持つ代わりに、長さプレフィックス付きのバイト列(ワイヤフォーマット)としてそのまま保存する方式に変更。enum のタグやパディングによるオーバーヘッドがなくなるうえ、メモリ上でデータが連続して並ぶのでキャッシュ局所性も改善して、キャッシュへの挿入スループットが 13% 向上したんだって。
こうした積み重ねの結果、1 エントリあたりのメモリ割り当ても 1.1 キロバイトから 461 バイトへと 58% 減ったの。地道な構造体レベルのチューニングが、フリート規模だと 100 テラバイト単位のインパクトになるっていうのが、この記事のいちばん痺れるところだと思う。
まとめ
- Cloudflare の DNS キャッシュ基盤「Big Pineapple」が 5 つの Rust レベルの最適化で 1 エントリあたりのメモリを 953 バイトから 420 バイトへ 56% 削減
- フリート全体で約 100 テラバイト、Gen 13 サーバー 130 台分相当のメモリを解放
- Vec/String の容量フィールド削除、リスト統合、所有者名の差分化、enum のボックス化、ワイヤ形式保存という 5 つの工夫を積み重ねた成果
- キャッシュ挿入スループットは 43% 向上、検索レイテンシは 19% 短縮、本番メモリ使用量(p99)は 43% 減少
- 浮いたメモリはキャッシュ容量の拡大に再投資され、ヒット率向上を狙う方針
Rust でメモリレイアウトをどう詰めるか、その具体的な手口を知りたいバックエンド・インフラ寄りのエンジニアにはたまらない一本だよ。