代表的な面接トピック

バックエンド面接:安全な stale-while-revalidate キャッシュコントラクトをどのように設計しますか?

バックエンド難しい
Offer.cc 編集チーム公開日 更新日

質問

読み取り主体の API で、オリジンの低速時に低レイテンシを維持する必要があります。stale-while-revalidate と stale-if-error をどのように使い分け、どのような場合に古い(stale)データの提供を拒否しますか?

プロンプトと設定

CDN およびアプリケーションキャッシュの背後で GET /catalog を運用しています。デプロイ中にオリジンが低速になることがありますが、顧客は空白のページが表示されるよりもわずかに古いカタログが表示される方を好みます。テナントデータを隔離し、鮮度を測定可能な状態に保ちながら、キャッシュヘッダーと再検証パスを設計してください。

カタログのレスポンスはテナントごとに公開され、書き込みはオリジンを経由し、緊急の価格変更は迅速に反映される必要があると仮定します。回答では、可用性のためのフォールバックと、機密性の高い状態の提供を許可することの違いを明確に区別する必要があります。

面接官がテストしていること

  • 鮮度(freshness)、陳腐化(staleness)、再検証(revalidation)、および独立した stale-if-error ポリシーを理解しているか。
  • キャッシュキーがテナント、認証、ロケール、コンテンツネゴシエーションによって適切に変化(vary)しているか。
  • 同時発生したキャッシュミスが、単一のオリジンリクエストになるか、それともサンダリングハード(thundering herd)を引き起こすか。
  • オペレーターが古いデータの提供を無効化でき、メトリクスによって鮮度を証明できるか。

回答前に確認すべき明確化の質問

  1. レスポンスはパブリック、テナント単位、またはユーザー固有のどれですか?プライベートデータを CDN で共有することはできません。
  2. 通常の読み取り時およびオリジン障害時において、許容される最大経過時間(age)はどれくらいですか?これらはそれぞれ独立した鮮度ウィンドウおよび stale ウィンドウになります。
  3. 価格や権限の変更時にオブジェクトを即時無効化できますか?可能な場合は、TTL だけに頼るのではなく、パージまたはバージョニングされたキーを追加します。
  4. バリデータは利用可能ですか?ETag または Last-Modified により、完全な再取得から条件付き再検証へと変更できます。

30秒の回答フレームワーク

「鮮度ウィンドウ、上限のある stale-while-revalidate ウィンドウ、および独立した stale-if-error ウィンドウを定義します。キャッシュキーにはすべての表現および認可境界を含め、ユーザー固有のデータは private とします。stale ヒット時は迅速に応答を返しつつバックグラウンドで単一の条件付きリクエストをトリガーし、同時発生するミスは統合(coalesce)します。緊急の変更にはキーのパージまたはバージョニングを行います。メトリクスでは経過時間、再検証結果、stale-if-error の使用状況、テナント間漏洩テストを可視化し、オペレーターが stale 配信を無効化できるようにします。」

ステップごとの詳細解説

1. 鮮度と可用性の分離

max-age は、保存されたレスポンスがどれだけの期間フレッシュであるかを定義します。stale-while-revalidate は、バックグラウンドで再検証を行っている間、キャッシュが制限された期間内で古いレスポンスを提供することを許可します。stale-if-error は、オリジンエラー時用の独立した可用性許容設定です。どちらのディレクティブも古いデータを正しいものにするわけではなく、must-revalidate や該当する no-cache ルールを持つレスポンスを不用意に再利用することはできません。

パブリックなカタログでは、短い鮮度ウィンドウと、上限が設定された長めの stale ウィンドウを使用できます。権限エンドポイント、口座残高、または緊急の価格設定では、privateno-store、パージパス、またははるかに厳格なポリシーを使用する必要があります。ウィンドウを決定するのはキャッシュのデフォルトではなく、ビジネスリスクです。

2. 安全なキャッシュキーとレスポンスコントラクトの構築

キーには、テナント、ロケール、エンコーディング、および Vary で指定されたすべてのリクエストヘッダーを含める必要があります。表現が明示的にパブリックであり認可から独立していない限り、認証済みレスポンスが共有キャッシュに入ることを許可してはなりません。レスポンスにはそのポリシーを明確に記述できます。

http
Cache-Control: public, max-age=30, stale-while-revalidate=120, stale-if-error=600
Vary: Accept-Encoding, Accept-Language, X-Tenant-ID
ETag: "catalog-tenant-7-v42"

サーバーは、X-Tenant-ID が任意のクライアント値ではなく、認証されたルートまたはホストから導出されていることを検証する必要があります。テナント境界をキー内で安全に表現できない場合は、共有キャッシングを無効化してください。

3. サンダリングハードを起こさない再検証

stale ヒット時、保存されているボディを返し、キャッシュキーごとに1つの再検証をキューに入れます。1万人のリーダーが1万件のオリジン呼び出しを発生させないよう、短いロックまたは single-flight マップを使用します。再検証処理は If-None-Match を送信し、304 Not Modified であればボディを置換せずに鮮度を更新し、新しい 200 であればオブジェクトとバリデータを置換します。

一時的なエラーで再検証が失敗した場合、古いオブジェクトは stale-if-error の制限内でのみ保持します。失敗と経過時間を記録します。タイマーを繰り返しリセットすることによって、stale ウィンドウを無制限に延長してはなりません。

4. 無効化の明示化

TTL はセーフティネットであり、緊急制御手段ではありません。価格や権限の変更時は、バージョニングされた無効化イベントを発行するか、影響を受けるキーをパージする必要があります。書き込みパスは、イベントを発行する前に新しいバージョンをコミットできます。コンシューマーは冪等であり再試行可能である必要があります。パージが確認できない場合、API は短い must-revalidate 期間を設定するか、影響を受けるテナントのキャッシュをバイパスします。

5. ポリシーの計測と運用

レスポンスの経過時間、フレッシュヒット率、stale-while-revalidate ヒット率、stale-if-error カウント、再検証レイテンシ、304 率、オリジンエラー率、ロック競合、およびキャッシュキーのカーディナリティを追跡します。経過時間が最大値に近づいた場合、stale-if-error が予期せず急増した場合、テナント間のテストが失敗した場合にアラートを発報します。

stale レスポンスの提供を停止するための機能フラグまたはルートレベルのキルスイッチを用意します。コールドミス、同時の stale ヒット、オリジンのタイムアウト、バリデータの変更、パージの競合、テナントヘッダー、ロケールバリアント、および緊急の価格更新をテストします。テストオラクルとなるのは、単なるレイテンシの短縮ではなく、鮮度と隔離のコントラクトです。

質の高い模範解答

まずデータの分類から始めます。パブリックでテナントスコープのカタログには、max-age=30stale-while-revalidate=120、および個別に妥当性が確認された stale-if-error=600 を設定できます。ユーザー固有または権限に関するデータは private またはキャッシュ不可にする必要があります。キーにはテナントと表現のディメンションを含め、レスポンスにはバリデータを付与します。

stale ヒット時にはボディを提供し、キーごとに1つの条件付き再検証を実行します。304 は鮮度を更新し、200 はオブジェクトを置換します。オリジンの一時的な障害では、境界が定められた stale-if-error ウィンドウを使用できますが、タイマーを無限にリセットしてはなりません。書き込み時は、緊急の変更に対して冪等なパージまたはバージョンイベントを発行します。経過時間、stale の使用状況、再検証結果、およびテナントの隔離を監視し、stale 配信用のキルスイッチを備えます。

よくある間違い

  • 間違い: stale-while-revalidate をアカウントや権限データに適用する → 失敗の理由: 高速な stale レスポンスによって無効な認可決定が公開される可能性がある → 修正方法: 機密データは private またはキャッシュ不可に保つ。
  • 間違い: キーからテナントやロケールを省略する → 失敗の理由: ある表現が別の境界に提供されてしまう可能性がある → 修正方法: すべてのキーディメンションを導出しテストする。
  • 間違い: 再検証に失敗するたびに stale タイマーをリセットする → 失敗の理由: 障害時のデータが永久に残ってしまう可能性がある → 修正方法: 絶対的な stale 期限を強制する。
  • 間違い: stale の読み取りごとに1つのオリジンリクエストを送信する → 失敗の理由: stale へのバーストアクセスがサンダリングハードを引き起こす → 修正方法: キーごとに single-flight 再検証を使用する。
  • 間違い: TTL を緊急時の無効化手段として扱う → 失敗の理由: 緊急の変更が期限切れまで反映されない → 修正方法: パージまたはバージョンイベントを発行し、完了を確認する。

フォローアップの質問と回答

なぜ stale-if-error だけを使用しないのですか?

オリジンリクエストがエラーに遭遇したときにしか機能しないためです。stale-while-revalidate は、正常な更新が実行されている間に古いレスポンスを提供することで、通常のレイテンシを改善します。これらは異なる状況を解決するものであり、別個の境界とメトリクスが必要です。

パージ中にバリデータが変更された場合はどうなりますか?

オブジェクトをバージョニングし、パージイベントを冪等にします。古いバリデータを参照した再検証が、新しいバージョンを上書きしてはなりません。キャッシュエントリを置換する前に、オブジェクトのバージョンまたはコミットのタイムスタンプを比較します。順序が不確かな場合は、そのキーのキャッシュを一時的にバイパスします。

CDN は Vary: Authorization を持つ認証済みレスポンスをキャッシュできますか?

一部のシステムでは技術的に可能ですが、非常にリスクの高い設計です。private またはテナント公開用の明示的な表現を使用することが推奨されます。共有キャッシュが不可避な場合は、キー、認可の独立性、パージ、およびテナント間の隔離を、統合テストと運用統制によって証明してください。

公開情報ソース

関連する質問