INDEX

Workerの前にキャッシュを置けるようになったよ!ヒットしたらCPU課金ゼロなの

やっほー、しぃちゃんだよ!今日はCloudflareのブログで見つけたニュースを紹介するね。「Workerが自分専用のキャッシュを前に持てるようになった」っていう発表なんだけど、地味そうに見えて実はコストにもパフォーマンスにも直結するすごい話なの。さっそく見ていこう!

なにが発表されたの?

発表されたのは「Workers Cache」っていう新機能。Workerのエントリーポイントのすぐ手前に、地域ごとに階層化されたキャッシュを差し込めるようになったんだって。設定はWranglerの設定ファイルにたった1行足すだけでよくて、あとは組み合わせ自由(コンポーザブル)に使えるのが特徴なの。

しかも設定方法が親しみやすくて、Cloudflareが今まで使ってきた Cache-Control みたいな標準のHTTPヘッダーをそのまま活用する形になっているんだ。専用のダッシュボード設定とかを新しく覚える必要がないの、これは嬉しいポイントだよね。

そして一番のハイライトはここ。キャッシュがヒットしたときはWorkerのコード自体が実行されないから、CPU時間の課金が発生しないんだって。もちろんリクエストとしてはカウントされるけど、レンダリングのための計算コストはゼロになるの。

今までどうだったの?

Workersが2017年に登場したときは、「キャッシュとオリジンの手前にWorkerが立つ」っていう構成が基本だったの。つまりリクエストはまずWorkerに届いて、そこからキャッシュやオリジンに問い合わせる、っていう役割分担だったんだ。

でも最近はAstroやNext.js、RemixみたいなフレームワークがこぞってCloudflare向けのアダプターを用意するようになって、Worker自体がコンテンツを生成する「オリジン」になるケースがどんどん増えてきたの。

そうすると、本当はキャッシュしても問題ないはずのリクエストでも、キャッシュの仕組みがWorkerより手前になかったせいで、毎回律儀にWorkerのコードが動いてレンダリングし直しちゃう、っていうもったいない状態になっていたんだって。せっかく同じ答えになるとわかっているのに、そのたびに計算コストを払っていたイメージだね。

これからどうなるの?

Workers Cacheが入ると、この順番が「キャッシュ → Worker → オリジン」にひっくり返るの。ゾーン単位の設定を別途管理する必要はなくて、Worker自身が返す Cache-Control ヘッダーがそのまま設定になる、っていうシンプルな考え方なんだって。

これで、サーバーサイドレンダリングなアプリを動かしている開発者さんは、同じ結果を返せるリクエストについてはキャッシュヒットのたびにコストゼロで応答できるようになるの。パフォーマンスも上がるし、お財布にも優しくなる、まさに一石二鳥な仕組みだね。

Dive Deep

ここからはしっかり技術的な中身を見ていくよ、しぃちゃん気合入れちゃう!

2階層構造のキャッシュ: Workers Cacheは「地域階層型(regionally tiered)」のキャッシュで、下層と上層の2段構えになっているの。下層はユーザーに一番近いCloudflareのデータセンター内のキャッシュ、上層はネットワーク全体のフィル(埋め込み)を集約する層なんだって。リクエストはまず下層をチェックして、ミスしたら上層、そこでもミスして初めてWorkerが実行される流れ。世界のどこかで最初の1回リクエストが上層を埋めると、それ以降は世界中どこからのリクエストでも上層からサーブできるようになるから、単純な1層構成のキャッシュよりずっと高いヒット率を狙える設計なんだ。

設定はほぼこれだけ:

{
  "name": "my-worker",
  "cache": { "enabled": true }
}

これをWranglerの設定に足すだけでキャッシュが有効になるの。

レスポンス側のヘッダー例:

return new Response(body, {
  headers: {
    "Cache-Control": "public, max-age=300, stale-while-revalidate=3600",
    "Cache-Tag": "products,product:123"
  }
});

max-age はキャッシュが新鮮とみなされる秒数、stale-while-revalidate は期限切れ後も一旦古いコピーを返しながら裏側でこっそり更新する仕組み、Cache-Tag はあとでまとめてパージするための識別子として使うんだって。同じURLでも複数のバリエーション(WebP版とJPEG版とか)を出し分けたいときは Vary ヘッダーで対応できるみたい。

パージもコードから呼べる:

await ctx.cache.purge({ tags: ["product:123"] });

特定のタグに紐づくキャッシュだけをまとめて消せるの。

マルチテナント対応: サービスバインディング経由でWorkerを呼ぶとき、ctx.props にユーザーIDなどを載せておくと、それがキャッシュキーの一部として組み込まれて、ユーザーごとに別々のキャッシュエントリが保証されるんだって。認証が絡むAPIでも安全にキャッシュできる作りになっているの。

entrypointごとに有効・無効を切り替え: 1つのWorkerが複数のnamed entrypointを持っている場合、exports のマップで個別にキャッシュのオン・オフを設定できるんだって。たとえば認証を担当するentrypointはキャッシュ無効のままにして、計算コストの高いバックエンド処理を行うentrypointだけキャッシュを有効にする、みたいな使い分けができるの。

zoneじゃなくてWorker単位: 従来のCache RulesやPage Rulesはゾーン(hostname)単位の設定だったけど、Workers Cacheは文字通りWorkerに紐づく設定。管理すべきゾーン設定がそもそも存在しなくて、Workerが返すヘッダーがそのまま設定になるの。だからworkers.dev上でも使えるし、Workers for Platformsでテナントごとに分かれている場合もきちんと隔離される仕組みになっているみたい。

現時点の制限: ローンチ時点では、全プラン共通でFreeプラン相当のキャッシュ可能サイズ上限(512 MB)が適用されるんだって。これは今後、段階的な展開の中でプラン別の上限に変わっていく予定とのこと。

料金の考え方: キャッシュヒット時はリクエスト課金だけで、CPU時間の課金は発生しないの。ミスやバイパスの場合は通常通りリクエスト+CPU時間が課金対象。キャッシュ機能そのものに追加のSKUはなくて、Tiered Cacheやパージ、分析を使っても追加料金はかからないんだって。ただし静的アセットへのリクエストやサービスバインディング経由のWorker間呼び出しは、それぞれ通常のリクエストレートで課金される点は覚えておきたいところ。

利用可能な範囲: Workers Cacheは発表時点で、プランを問わずすべてのWorkerで使えるようになっているんだって。

これからの計画: Smart Placementとの組み合わせ最適化、キャッシュ可能なレスポンスサイズ上限の拡大、Astro以外(TanStack StartやNext.jsなど)のフレームワーク統合、そして完全削除ではなく「期限切れ」扱いにする ctx.cache.invalidate() の追加などが今後の予定として挙がっているよ。

まとめ

Workerの前に専用のキャッシュを配置できるようになったことで、キャッシュヒット時はWorkerのコードが動かずCPU課金もかからない、っていうのが今回の一番のポイントだったね。設定もCache-Controlヘッダーとほんの数行のWrangler設定だけで済むから、フレームワーク経由でWorkerがオリジン化している今のトレンドにすごくマッチしているなってしぃちゃんは思ったよ。パフォーマンスとコストの両方が改善するこの仕組み、今後の拡張も楽しみにしていようね!

原文はこちら: Your Worker can now have its own cache in front of it