LCL Engineers' Blog

バス比較なび・格安移動・バスとりっぷを運営する LCLの開発者ブログです。

Varnish Grace Modeで非同期にキャッシュを更新する

Webエンジニアの森脇です。

先日、Inside Frontend というフロントエンドのイベントがあり、チームメンバーが参加しました。参加メンバーから日経新聞社様の「日経電子版を速くする」について共有をしてもらい、弊社でも活用できそうな点は、取り入れさせて頂くことにしました。

日経電子版を速くする / nikkei-inside-frontend - Speaker Deck

資料の中で、Fastlyの「キャッシュの非同期更新」について紹介されています。弊社では、FastlyではなくVarnishを利用していますが、VarnishのGrace Modeでも同等のことが実現可能のため、今回は簡単な検証を兼ねて紹介します。

LCLでのVarnish活用については、以下の記事で紹介しています。

techblog.lclco.com

Grace Modeとは

キャッシュ有効期間(TTLで設定された期間)が過ぎた場合に置いても、 grace期間はキャッシュからレスポンスを返し、バックグラウンドでフェッチし、キャッシュオブジェクトを更新します。

これにより、クライアントにはなるべくキャッシュからレスポンスを返すとともに、キャッシュのオブジェクトを最新化できます。

図にするとこのような動きになると認識しています。

f:id:lcl-engineer:20180220112240p:plain

検証

graceの動きを簡単に検証します。

Varnishバージョン

varnish 4.1.9

シナリオ

  • backendサーバ側では、現在時刻を表示するHTMLを返す
  • ttl、graceは30sとする

vcl

sub vcl_backend_response {
  set beresp.ttl = 30s;
  set beresp.grace = 30s;
}

初回アクセス

初回アクセスのため、backendからHTMLを取得し現在時刻が表示されます。

f:id:lcl-engineer:20180220115013p:plain:w200

約25秒後に再アクセス

初回アクセスから30s以内のため、キャッシュされた時点での時刻が表示されます。

f:id:lcl-engineer:20180220115013p:plain:w200

ttl: 4.556

約35秒後に再アクセス

初回アクセスから30sを過ぎていますが、grace期間内のためキャッシュされた時点での時刻が表示さます。

f:id:lcl-engineer:20180220115013p:plain:w200

// 30sを過ぎているので、ttlはマイナスとなる
ttl: -5.069

この時、バックグラウンドでフェッチされ、キャッシュが更新されます。

約36秒後にアクセス

前回のアクセスによって、キャッシュが更新されているため、前回アクセス日時が表示されます。

f:id:lcl-engineer:20180220115711p:plain:w200

以上、簡単ですがgraceの動きを検証しました。

他ユースケースの紹介

次に、いくつかのユースケースに応じたVCLの定義を紹介します。

backendがエラーの場合のみ、graceを利用する

backendの状態によって、graceの利用有無を制御することも可能です。

まず、grace期間を1Hと長めに設定しておきます。

sub vcl_backend_response {
  set beresp.grace = 1h;
}

vcl_hitで、backendの状態を見て、graceの利用有無を制御します。

sub vcl_hit {
    if (obj.ttl >= 0s) {
        // キャッシュを利用する
        return (deliver);
    }
    if (!std.healthy(req.backend_hint) && (obj.ttl + obj.grace > 0s)) {
      // backendがエラーの場合、grace期間内はキャッシュを利用する
      return (deliver);
    } else {
      return (fetch);
    }
}

リクエストURLに応じて、grace期間を分ける

リクエストURLに応じて、graceの設定を変更することも可能です。

以下の例では、デフォルトは30sでトップページのみ300sにしています。

sub vcl_recv {
  if (req.url == "/") {
    set req.http.X-set-grace = "300s";
  }
}

sub vcl_backend_response {
  set beresp.grace = std.duration(bereq.http.X-set-grace,30s);
}

まとめ

graceをうまく利用することで、以下の要望をある程度は満たすことができます。

  • キャッシュからレスポンスを返したい
  • 新しいオブジェクトを返したい

ですが、適切なTTL/graceを設定するのはやはり難しく、弊社でも必要に応じて随時調整しています。今後も、継続的にパフォーマンス向上に取り組んでいきます。