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 の観点から、このシナリオでできることは何もありません。

ただし、キャッシュに保存されたレスポンスが古い場合は、別の age ベースのチェックが実行されます。キャッシュに保存されたレスポンスを stale-while-revalidate 設定で指定された追加の時間枠内に収める必要があります。

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

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

ライブ例

以下に、現在の時刻(具体的には、現在の時刻から経過した分数)を返す HTTP API の簡単な例を示します。

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

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

この設定では、1 秒以内に時刻のリクエストが繰り返された場合、以前にキャッシュに保存された値は引き続き新鮮であり、再検証なしでそのまま使用されます。

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

60 秒以上経過してからリクエストが繰り返された場合、古いレスポンスはまったく使用されず、ブラウザのリクエストの処理とキャッシュの再検証は、ネットワークからレスポンスが返されるかどうかに依存します。

次の表に、この例で各状態が適用される時間枠とともに、3 つの異なる状態の詳細を示します。

前のセクションの情報を示した図。

一般的なユースケース

上記の「分単位の API サービス」の例は架空のものですが、更新が必要な情報を提供し、ある程度の古さは許容されるサービスという、想定されるユースケースを示しています。

あまり不自然でない例としては、現在の天候の API や、過去 1 時間に作成されたトップニュースの見出しなどがあります。

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

サービス ワーカーとどのように連携しますか?

stale-while-revalidate について聞いたことがある場合は、サービス ワーカー内で使用されるレシピのコンテキストで聞いた可能性があります。

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

次の場合は、サービス ワーカー アプローチを使用します。

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

次の場合に Cache-Control アプローチを使用します。

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

Service Worker を使用しており、Cache-Control ヘッダーを介して一部のレスポンスに対して stale-while-revalidate が有効になっている場合、通常、Service Worker がリクエストへの応答の「優先権」を持ちます。サービス ワーカーが応答しないことを決定した場合、またはレスポンスの生成中に fetch() を使用してネットワーク リクエストを行った場合、Cache-Control ヘッダーで構成された動作が最終的に有効になります。

その他の情報

ヒーロー画像: Samuel Zeller によるもの。