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

Fastly では、オリジンサーバーに問題が発生している時、またはオリジンサーバーからの新しいコンテンツの取得に長い時間を要している場合に、失効済みコンテンツを配信するよう設定することができます。例えば、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. Home ページから、適切なサービスを選択します。検索ボックスで 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 を送信している場合は、この行全体を削除します。
  • set beresp.stale_while_revalidate = 60s;これは、オブジェクトstale_while_revalidateに対して機能が有効になる時間を制御するもので、設定に意味のある量に設定する必要があります。この機能により、Varnish はキャッシュミスの際に執行済みとなり、バックグラウンドでオリジンから最新バージョンのオブジェクトを取得するようになります。これにより、TTL の短いオブジェクトや、一般的なキャッシュミスの際に、大きなパフォーマンスの向上が期待できます。 stale_while_revalidateは、 stale_if_errorによって上書きされる ことに注意してください。stale_if_errorつまり、オブジェクトが再検証中に失効済み(キャッシュしてからTTLに設定された時間が経過した)状態で配信される資格がある限り、 の効果はありません。オリジンから 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 にてキャッシュされることもあります。
  • 最近使用していない場合(LRU): Fastly は LRU リストを採用しているため、オブジェクトは TTL (キャッシュ保持時間) の期間中に継続的にキャッシュを保持することを必ずしも保証しません。キャッシュの保持と削除は、ファイルに対するリクエストの頻度、TTL の値、ファイルの配信元となる POP など、数多くの要因を踏まえて行われます。例えば、3700秒以上の TTL のファイルがディスクへ保管される一方で、3700秒未満の TTL の場合はメモリーのみに一時的に保管されます。TTL は可能な限り3700秒以上に設定することをお勧めします。
  • パージ: パージの際は、可能な限りソフトパージを実行してコンテンツをバージしください。ソフトパージでは、Fastly のキャッシュからコンテンツを永久に削除するのではなく、古い (失効済み) コンテンツとして簡単にマークすることができます。ソフトパージが不可能ならば、全コンテンツのパージをできる限り避けて、URL によるパージキーによるパージの活用を推奨します。
Back to Top