失効済みコンテンツの配信

Fastly では、オリジンサーバーに問題が発生している 時、またはオリジンサーバーからの新しいコンテンツの取得に長い時間を要している 場合に、失効済みコンテンツ(キャッシュしてからTTLに設定された時間が経過したコンテンツ)を配信するよう設定することができます。例えば、Fastly からオリジンサーバーへの接続が失敗する場合、Fastly POP はユーザーのリクエストに対してキャッシュ済みコンテンツの配信を続けます。これらの機能は既定では有効になっていません。

新しいコンテンツの取得中に古いコンテンツを配信する

コンテンツの種類によっては、生成に時間がかかるものもあります。一度キャッシュされたコンテンツはすぐに配信されますが、最初にアクセスしようとしたユーザーはコンテンツが生成されるのを待たなければなりません。

キャッシング時間のペナルティ

これはコールドキャッシュの場合には避けられないことですが、オブジェクトがキャッシュにあり、 TTL が期限切れになっている時にこのようなことが起こるのであれば、Fastly は新しいコンテンツがバックグラウンドでフェッチされている間、失効済みコンテンツを表示するように設定することができます。

バックグラウンドで新しいコンテンツを取得する

Fastly は、Google Chrome ブラウザへの搭載が検討されている Mark Nottingham 氏の RFC 5861 『HTTP Cache-Control Extensions for Stale Content』 で提案された動作をベースにしています。

失効済みの配信を有効にする

コントロールパネルを介したエラーで、デフォルトの TTL 期間 (43200秒または12時間) での失効済みコンテンツの配信を有効にするには、以下の手順に従ってください。

  1. Fastly コントロールパネルにログインし。
  2. All services ページから適切なサービスを選択します。検索ボックスを使用すると、ID、名称、ドメインでの検索が行えます。
  3. Edit configuration ボタンをクリックし、オプションを選択してアクティブバージョンをクローンします。設定画面が開きます。
  4. Settings のリンクをクリックし、Settings 設定画面を開きます。

    失効済みの配信をオンにする

  5. Serve stale スイッチをオンにすると、デフォルトの TTL 期間である43200秒 (12時間) の間、失効済みコンテンツの配信が自動的に有効になります。
  6. Activate ボタンをクリックしてサービスをデプロイします。

手動で失効済みの配信を有効にする

オリジンサーバーからのレスポンスの Cache-Control または Surrogate-Control ヘッダーに stale-while-revalidate または stale-if-error ディレクティブを追加することで、手動で失効済みコンテンツの配信を有効にすることができます。例:

Cache-Control: max-age=600, stale-while-revalidate=30

この例では、一部のコンテンツを10分間キャッシュした後、新しいコンテンツが取得されるまでの間、最大30秒間失効済みコンテンツを配信するようになっています。

同様に、 こちらのステートメント

Surrogate-Control: max-age=3600, stale-if-error=86400

では、キャッシュのコンテンツを1時間 (3600 秒) 毎に更新しますが、オリジンがダウンしている場合は期限切れのコンテンツを1日間 (86400 秒) 表示するようになっています。

また、vcl_fetch にて以下の変数を設定することで、VCL 内からこれらの動作を制御することもできます。

set beresp.stale_while_revalidate = 30s;

set beresp.stale_if_error = 86400s;

grace による影響

Stale-if-error は、Varnish の変数 grace と全く同じ働きをするので、この2つの記述は同等です。

set beresp.grace = 86400s;

set beresp.stale_if_error = 86400s;

ただし、VCL に grace のステートメントがある場合は、Cache-Control や Surrogate-Control のレスポンスヘッダー内の stale-if-error ステートメントを上書きします。

ヘッダーや VCL で beresp.stale_if_error を設定するだけでは何の意味もありません。失効済みコンテンツを配信するためには、以下の手順を踏む必要があります。

エラー発生時に失効済みコンテンツを配信する

オリジンサーバーが利用できなくなった場合に、失効済みコンテンツを配信したい場合があります。この手順では、3つのオリジン障害のシナリオにて VCL を使って対応するための高度な設定を行います。

Varnish の場合、オリジン障害には3つのケースが考えられます。

  • ヘルスチェックの失敗により、オリジンに異常があると見なされる。
  • 何らかの理由で Varnish がオリジンに接続できない場合、503エラーが発生する。
  • オリジンからの HTTP レスポンスが、ユーザーへの配信に適さない内容である (503エラーなど)。

以下のカスタム VCLは、この3つのケースに対応することができます。オリジンが不健全な場合は、stale-if-error によってデフォルトの失効済みの配信動作が起動します。オリジン障害の発生からオリジンのヘルスステータスが異常と見なされるまでの間、Varnish は通常503 エラーを返します。カスタム VCL を使用することで、失効済みのコピーがある場合には、代わりに失効済みを配信するか、シンセティックエラーページを返すことができます。エラーページはカスタマイズできます。3つ目のケースでは、vcl_fetch での 5XX エラーを遮断し、失効済みコンテンツを配信するか、シセティックエラーページを配信することで対応できます。

厳密には必要ありませんが、この VCL と併せてヘルスチェックを有効にすることをお勧めします。ヘルスチェックを有効にしていない場合でもすべての機能は動作しますが、失効済みのレスポンスやシンセティックレスポンスの配信には、オリジンのタイムアウトを待たなくてはいけないため、より長い時間がかかります。ヘルスチェックを有効にすると、オリジンが不健全であるとマークされるため、この問題は回避されます。

以下のカスタム VCL は、Fastly 標準のボイラープレートを含んでいます。お客様のサービスにアップロードする前に、必要に合わせて以下の値をカスタマイズまたは削除してください。

  • if (beresp.status >= 500 && beresp.status < 600) は、失効済みコンテンツやシンセティックレスポンスを配信すべき HTTP レスポンスコードを含むように変更する必要があります。
  • set beresp.stale_if_error = 86400s; は、失効済みコンテンツが配信される期間を制御します。この値は、設定に応じて適切な値に設定する必要があります。オリジンから Surrogate-Control または Cache-Control にて stale_if_error を送信している場合は、この行全体を削除します。
  • は、オブジェクトに対して stale_while_revalidate 機能が有効にされる時間を制御するもので、設定に合わせて適切な時間を設定する必要があります。この機能により、Varnish はキャッシュミスの際に失効済みを配信し、バックグラウンドでオリジンからオブジェクトの最新バージョンを取得します。stale_while_revalidateこれにより、TTL の短いオブジェクトや、一般的なキャッシュミスの際に、大きなパフォーマンスの向上が期待できます。 は、 によって上書きされる stale_if_errorstale_if_error ことに注意してください。つまり、オブジェクトが再検証中に失効した状態で配信される資格がある限り、 の効果はありません。オリジンから Surrogate-Control または Cache-Control にて stale_while_revalidate を送信している場合は、この行全体を削除します。
  • synthetic {"<!DOCTYPE html>Your HTML!</html>"}; は、失効済みバージョンのオブジェクトが存在しない場合に Varnish が返すシンセティックレスポンスであり、設定に応じて適切に設定する必要があります。ここには、お客様の HTML、CSS、JS を埋め込むことができます。外部の CSS や JS ドキュメントを参照する際には注意が必要です。オリジンがオフラインの場合は、それらも利用できない可能性があります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
sub vcl_recv {
  /* if shielding is enabled, the below code is required */
  if (fastly.ff.visits_this_service != 0) {
    set req.max_stale_while_revalidate = 0s;
  }

#FASTLY recv

  if (req.method != "HEAD" && req.method != "GET" && req.method != "FASTLYPURGE") {
    return(pass);
  }

  return(lookup);
}

sub vcl_fetch {
  /* handle 5XX (or any other unwanted status code) */
  if (beresp.status >= 500 && beresp.status < 600) {

    /* deliver stale if the object is available */
    if (stale.exists) {
      return(deliver_stale);
    }

    if (req.restarts < 1 && (req.method == "GET" || req.method == "HEAD")) {
      restart;
    }

    /* else go to vcl_error to deliver a synthetic */
    error beresp.status;
  }

  /* set stale_if_error and stale_while_revalidate (customize these values) */
  set beresp.stale_if_error = 86400s;
  set beresp.stale_while_revalidate = 60s;

#FASTLY fetch

  if ((beresp.status == 500 || beresp.status == 503) && req.restarts < 1 && (req.method == "GET" || req.method == "HEAD")) {
    restart;
  }

  if (req.restarts > 0) {
    set beresp.http.Fastly-Restarts = req.restarts;
  }

  if (beresp.http.Set-Cookie) {
    set req.http.Fastly-Cachetype = "SETCOOKIE";
    return(pass);
  }

  if (beresp.http.Cache-Control ~ "private") {
    set req.http.Fastly-Cachetype = "PRIVATE";
    return(pass);
  }

  /* this code will never be run, commented out for clarity */
  /* if (beresp.status == 500 || beresp.status == 503) {
     set req.http.Fastly-Cachetype = "ERROR";
     set beresp.ttl = 1s;
     set beresp.grace = 5s;
     return(deliver);
  } */

  if (beresp.http.Expires || beresp.http.Surrogate-Control ~ "max-age" || beresp.http.Cache-Control ~ "(s-maxage|max-age)") {
    # keep the ttl here
  } else {
    # apply the default ttl
    set beresp.ttl = 3600s;
  }

  return(deliver);
}

sub vcl_hit {
#FASTLY hit

  if (!obj.cacheable) {
    return(pass);
  }
  return(deliver);
}

sub vcl_miss {
#FASTLY miss
  return(fetch);
}

sub vcl_deliver {

#FASTLY deliver
  return(deliver);
}

sub vcl_error {
#FASTLY error

  /* handle 503s */
  if (obj.status >= 500 && obj.status < 600) {

    /* deliver stale object if it is available */
    if (stale.exists) {
      return(deliver_stale);
    }

    /* otherwise, return a synthetic */

    /* include your HTML response here */
    synthetic {"<!DOCTYPE html><html>Replace this text with the error page you would like to serve to clients if your origin is offline.</html>"};
    return(deliver);
  }

}

sub vcl_pass {
#FASTLY pass
}

sub vcl_log {
#FASTLY log
}

失効済みコンテンツの配信が期待通りにいかない理由

Fastly からの失効済みコンテンツの配信が行われない場合、以下のポイントを確認してくださいします。

  • キャッシュ: 失効済みオブジェクトは、キャッシュ可能なコンテンツにのみ有効です。
  • VCL: req.hash_always_missまたは req.hash_ignore_busy の変数を true にすると、stale-while-revalidate の効果が無効になります。
  • オリジンシールド: オリジンシールドを有効にしていない場合、POPは、そのキャッシュ可能なオブジェクトへのリクエストが以前にその POP を介して行われた場合にのみ、エラー時に失効済みコンテンツを配信することができます。エラー発生時に失効済みコンテンツがキャッシュされる確率を高めるため、オリジンシールドの有効化を推奨します。オリジンシールドは、前コンテンツのパージを実行した後に素早くキャッシュを補充するのにも適しています。
  • リクエスト: サイトへのトラフィックが増加すると、オリジンシールドが無効な場合でも、失効済みオブジェクトが利用可能になる可能性が高くなります。人気のあるコンテンツならば、複数の POP にてキャッシュされることもあります。
  • Least Recently Used (LRU): Fastly は LRU リストを採用しているため、オブジェクトは必ずしも TTL (キャッシュ保持時間) の期間中ずっとキャッシュを保持することを保証していません。キャッシュの保持と削除は、ファイルに対するリクエストの頻度、TTL の値、ファイルの配信元となるPOP など、数多くの要因を踏まえて行われます。例えば、3700秒以上の TTL のファイルがディスクへ保管される一方で、3700秒未満の TTL の場合はメモリーのみに一時的に保管されます。TTL は可能な限り3700秒以上に設定することをお勧めします。
  • パージ: 可能な限り、ソフトパージ機能を使用してコンテンツをパージしてください。ソフトパージでは、Fastly のキャッシュからコンテンツを永久に削除するのではなく、古い (失効済み) コンテンツとして簡単にマークすることができます。ソフトパージを使用できない場合は、全コンテンツのパージをできる限り避け、URL によるパージサロゲートキーによるパージの活用を推奨します。
Back to Top