stale-while-revalidate で最新の状態を維持する

ウェブアプリを提供する際に即時性と鮮度のバランスを取るための追加ツール。

発送内容

stale-while-revalidate を使用すると、デベロッパーは即時性(キャッシュに保存されたコンテンツをすぐに読み込む)と鮮度(キャッシュに保存されたコンテンツの更新が今後使用されるようにする)のバランスを取ることができます。定期的に更新されるサードパーティのウェブサービスまたはライブラリを維持している場合や、ファースト パーティ アセットの有効期間が短い傾向がある場合は、既存のキャッシュ ポリシーに stale-while-revalidate を追加すると便利です。

Cache-Control レスポンス ヘッダーの max-age とともに stale-while-revalidate を設定する機能は、Chrome 75Firefox 68 で利用できます。

stale-while-revalidate をサポートしていないブラウザは、その構成値を通知なく無視し、この後で説明するように max-age を使用します。

どういう意味ですか?

stale-while-revalidate を、キャッシュに保存されたレスポンスが古くなっている可能性があるという考え方と、再検証プロセスの 2 つの部分に分けて説明します。

まず、ブラウザはどのようにしてキャッシュされたレスポンスが「最新でない」かどうかを判断するのでしょうか。stale-while-revalidate を含む Cache-Control レスポンス ヘッダーには max-age も含まれます。未更新は、max-age で指定された秒数によって決まります。キャッシュに保存されたレスポンスのうち max-age より新しいものは新しいものと見なされ、キャッシュに保存されている古いレスポンスは最新でないものとみなされます。

ローカルにキャッシュされたレスポンスがまだ新しい場合は、そのまま使用してブラウザのリクエストを処理できます。stale-while-revalidate の観点からは、このシナリオでは何もする必要はありません。

ただし、キャッシュに保存されたレスポンスが古くなっている場合は、別の経過時間に基づくチェックが行われます。つまり、キャッシュに保存されたレスポンスの経過時間が stale-while-revalidate 設定で指定された追加の時間枠内に収まるかどうかです。

古いレスポンスの経過時間がこのウィンドウに該当する場合は、ブラウザのリクエストを満たすために使用されます。同時に、キャッシュに保存されたレスポンスの使用を遅延させない方法で、ネットワークに対して「再検証」リクエストが実行されます。返されるレスポンスには、以前にキャッシュに保存されたレスポンスと同じ情報が含まれていることもあれば、異なる情報が含まれていることもあります。どちらの場合も、ネットワーク レスポンスはローカルに保存され、以前にキャッシュに保存された内容と置き換えられて、以降の max-age の比較で使用される「鮮度」タイマーがリセットされます。

ただし、キャッシュに保存されている古いレスポンスが古く、stale-while-revalidate の時間枠を過ぎている場合、ブラウザのリクエストは処理されません。ブラウザは代わりにネットワークからレスポンスを取得し、そのレスポンスを使用して最初のリクエストの処理と、ローカル キャッシュへの新しいレスポンスの取り込みの両方を行います。

実際の例

以下は、現在の時刻、より具体的には 1 時間後の現在の分数を返す HTTP API の簡単な例です。

このシナリオでは、ウェブサーバーは HTTP レスポンスでこの Cache-Control ヘッダーを使用します。

Cache-Control: max-age=1, stale-while-revalidate=59

この設定により、次の 1 秒以内にその時間のリクエストが繰り返された場合、以前にキャッシュに保存された値は新しいままで、再検証されずにそのまま使用されます。

1 ~ 60 秒後にリクエストが繰り返されると、キャッシュに保存された値は古くなりますが、API リクエストを満たすために使用されます。同時に「バックグラウンドで」、再検証リクエストが実行され、キャッシュに今後使用するために新しい値が入力されます。

60 秒を超えてリクエストが繰り返されると、古くなったレスポンスはまったく使用されず、ブラウザのリクエストへの対応とキャッシュの再検証は、ネットワークからレスポンスを受け取るかどうかによって決まります。

以下に、これら 3 つの状態の内訳と、それぞれの状態がこの例で該当する期間を示します。

前のセクションの情報を表す図。

一般的なユースケース

上の「1 時間後」の API サービスの例は工夫されていますが、予想されるユースケースを示しています。これは、更新が必要な情報を提供するサービスですが、ある程度の陳腐化は許容されます。

現在の気象状況の API や、過去 1 時間に書かれたトップニュースの見出しなどは、単純ではないものの例として挙げられます。

一般に、既知の間隔で更新されるレスポンスは複数回リクエストされる可能性が高く、その間隔内で静的であるレスポンスは、max-age を介した短期キャッシュに適しています。max-age に加えて stale-while-revalidate を使用すると、ネットワーク レスポンスをブロックすることなく、今後のリクエストをより新しいコンテンツでキャッシュから処理できる可能性が高まります。

Service Worker とはどのようにやり取りしますか。

stale-while-revalidate について聞いたことがある方は、Service Worker 内で使用されるレシピのコンテキストで使った可能性があります。

Cache-Control ヘッダーを介して stale-while-revalidate を使用すると、Service Worker での使用と類似点があり、鮮度のトレードオフと最大存続期間に関する同じ考慮事項の多くが適用されます。ただし、Service Worker ベースのアプローチを実装するか、Cache-Control ヘッダー構成のみを使用するかを決定する際に、考慮すべき考慮事項がいくつかあります。

次のような場合は Service Worker のアプローチを使用します。

  • ウェブアプリで Service Worker をすでに使用している。
  • キャッシュの内容を細かく制御する必要があり、最も長く使われていない有効期限ポリシーなどを実装する必要がある。この問題には、Workbox の Cache Expiration モジュールが役立ちます。
  • 再検証ステップ中に、バックグラウンドで古いレスポンスが変更されたときに通知を受け取りたい場合。Workbox の Broadcast Cache Update モジュールがこれに役立ちます。
  • 最新のすべてのブラウザで、この stale-while-revalidate の動作が必要です。

キャッシュ制御のアプローチが適しているケース

  • ウェブアプリの Service Worker のデプロイと維持にかかるオーバーヘッドに対処したくない。
  • ローカル キャッシュが大きくなりすぎないように、ブラウザの自動キャッシュ管理で問題はありません。
  • 最新のすべてのブラウザで現在サポートされていないアプローチで問題ありません(2019 年 7 月現在、サポートは今後拡大される可能性があります)。

Service Worker を使用していて、Cache-Control ヘッダーを介して一部のレスポンスに対して stale-while-revalidate も有効にしている場合、Service Worker は通常、リクエストへのレスポンスで「ファースト クラック」を行います。Service Worker が応答しないと決定した場合、またはレスポンスの生成プロセスで fetch() を使用してネットワーク リクエストを行った場合、Cache-Control ヘッダーを介して構成された動作が有効になります。

詳細

Samuel Zeller 氏によるヒーロー画像。