4月 182022
 

先日とらのあなラボ様の勉強会に参加していたところ「強いキャッシュ」「弱いキャッシュ」とキーワードが出てきました。
初めて聞く表現だったので質問したところやはり知らない定義だったため、少し調べてまとめてみたものです。
なお、強いキャッシュ・弱いキャッシュという説明を否定するものではなく、補完したいと考えています。

強いキャッシュ・弱いキャッシュの定義

ネット上を調べると日本語・中国語・英語で説明が出てきますが、調べた限りでは強いキャッシュ・弱いキャッシュの初出はWebフロントエンド ハイパフォーマンスで、定義は以下の通りです。

ExpiresヘッダーとCache-Controlヘッダーでは強いキャッシュを設定できます。
ETagヘッダーとLast-Modifiedヘッダーでは弱いキャッシュを設定できます。

Webフロントエンド ハイパフォーマンス p124

こちらの文書の前後に詳しい定義がありますが、要約すると

  • 明示的にTTLを設定Cache-Control: max-age / Expiresすることで、キャッシュ利用時にオリジンにリクエストが飛ばないキャッシュを強いキャッシュ
  • キャッシュ利用時にオリジンに条件付き問い合わせを行う必要があるものを弱いキャッシュ

で、著者に質問をしたところ

  • 条件付きリクエストで検証が必要なキャッシュを弱いキャッシュ
  • それ以外は強いキャッシュ

で、またこの「強いキャッシュ・弱いキャッシュ」は本書で便宜的につけた名前でRFCなどには定義はないと思うとのことでした。

検証が必要なものを弱いキャッシュ・それ以外は強いキャッシュと呼ぶのは
直観的でわかりやすいなとは思うのですが、より詳しくキャッシュについて知ろうと思った場合にキャッシュの仕様(RFC7234)おける概念とどう紐づければいいのかというのを説明したいと思います。

キャッシュのRFCには強いキャッシュ・弱いキャッシュという表現はない

著者の回答通りなのですが RFC7234 Caching そろっと新しいRFC出ると思いますがにおいては強いStrongキャッシュ・弱いWeakキャッシュという表現はありません。

Strong/Weakの表現があるのは検証子Validatorで、今回の本筋ではないので詳しくは説明しませんが一般的にはLast-ModifiedとETagを指します。RFC7232#2

ETagを見ていると「etag: W/”6226c295-200f3″」のようにW/から始まるものと「etag: “6226c295-200f3″」W/がついていないものがあることに気づくと思います。ついてると弱い検証子です。(Weak validator)

強いキャッシュ・弱いキャッシュはキャッシュのRFCで言うところのなんなのか

まずは(HTTP)キャッシュの動きについてちょっと考えてみましょう。

サイトでCSSなどの静的リソースをキャッシュしたいということで、1日のTTLを設定するとします。

このケースでオリジンからレスポンスされるヘッダに含まれるキャッシュに関わるヘッダは以下の通りとします

  • Cache-Control: max-age=86400
  • ETagヘッダ
  • Last-Modifiedヘッダ

このようなヘッダであれば、サイトへの初回訪問時にキャッシュされ、少なくともその日にサイトを回遊してる間はキャッシュから利用できるでしょう。

そして、数日たってからそのサイトに再訪した時には当然TTLは切れています。

ですが、キャッシュというものはTTLが切れたからといって使い道がなくなって即時で消えるものではありません。

TTLが切れてはいるものの、そのキャッシュが最新のものと同一であることが確認できれば再利用できます。
この再利用を行うためにクライアントがオリジンに対して行うのが条件付きリクエストIf-None-Matchなどです。
この時に最新のものと同一であることを確認するのに使うのが検証子でETag/Last-Modifiedです。
要は今持ってるキャッシュはこの検証子だけど、最新の検証子と一致してる?と聞いてるわけです。
もし検証子が一致しないのであればオリジンは200を返却し、同時に本文を返します。
一致していれば304を返し、本文は返しません。本文の転送が発生しない分低コストなわけです。

もちろん条件付きリクエストはこの動作をするものがすべてではない(If-Matchなど)のですが、ブラウザでキャッシュ更新で使うIf-Modified-Since/If-None-Matchはこの動きをします。

一般的なキャッシュのざっくりとした動きはこんな感じですが、これを強い・弱いキャッシュの表現で考えてみましょう。

  • TTL期間中は強いキャッシュで、リクエストのタイミングでTTLが切れていれば検証が必要な弱いキャッシュ
  • 検証を行いTTLが延長されればまたしばらくは検証が不要となるため強いキャッシュとなる。

おそらくこんな感じの説明ができると思います。

次にRFC7234における表現で考えてみましょう。

RFC7234ではキャッシュの状態はFresh(新鮮)とStale(陳腐化してる)があります。

それぞれ

  • TTLが切れていない=Fresh
  • TTLが切れている=Stale

となります。

なお、Freshの場合は必ずキャッシュが使われるかというとそうでもなく、またStaleだとキャッシュが使われないかというとそうでもないのが厄介なところです。RFC7324#4

格納しているキャッシュを使えるかどうかを判定する一つの条件にFreshがあるだけです。

また、オリジンにリクエストを行ったセッションでのオブジェクトはFreshなのかというと難しいです。
そもそもそのオブジェクトはオリジンから持ってきたもので、キャッシュから持ってきたわけではないむしろ様々な条件でキャッシュしようとしているので別枠と考えるのが良いでしょう。(そのセッションのみではFreshとも考えられますがキャッシュを使ってるわけではないのでちょっと違和感がある)

これらを踏まえて同じように説明すると

  • TTL期間中はFreshで、リクエストのタイミングでTTLが切れていれば検証が必要なStale
  • 検証を行いTTLが延長されればまたしばらくは検証が不要となるためFreshとなる。

若干先ほどにあわせるために強引な説明正確でないにはなってますが、だいたいこんな感じです。

つまり、強い・弱いキャッシュはFresh・Staleの関係に近いというわけです。

もちろん厳密にいうと異なります。
ハイパフォーマンス本の強いキャッシュの定義には「Cache-Controlのmax-ageやExpiresで明示的にTTLを指定する」とありますが、TTLはこれらヘッダが未指定でもDateとLast-Modifiedヘッダがあれば有効になるヒューリスティックなTTLというものがあります。HTTPキャッシュ入門の入門参照

いうならば「強いキャッシュ」は「Fresh」に含まれるでしょうか

また、強いキャッシュと弱いキャッシュは相反するものではなく、今回の例のように明示的にTTLを指定しつつ(強いキャッシュ)、Etagを指定(弱いキャッシュ)することでTTL切れの時に弱いキャッシュとして動作することができると考えられます。

ここまでで、強いキャッシュ・弱いキャッシュをベースにキャッシュの仕様を読む際のとっかかり部分ができたかなと思っています。

より詳しくキャッシュを知りたい時に注意しておきたいところ

基本的には以前書いたHTTPキャッシュ入門の入門や自著のWeb配信の技術を読んでほしいのですが(今回の内容は全部3章にあります)
キャッシュというのは時間経過で状態がかわる(TTLがある)ので必ずしも格納したタイミングではそれがFreshともStaleのどちらかといったことはわかりません。

じゃぁどこでわかるのかというと、キャッシュを利用するタイミングです。

案外抜けがちだと思うのですが、キャッシュは「格納」「利用する」の2つのステージがあり、それぞれ別のロジックで動きます。

この「格納」「利用する」を分けて押さえておくと一気にいろいろ理解ができると思います。


 Posted by at 10:39 PM

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください