WebPへの変換は、1ファイルずつ見れば高速に終わる。それでも画像のフォーマット変換は軽い処理ではないし、何万・何十万ファイルとなれば相応の負荷になる。エンドユーザーには一瞬でも早く軽量化された画像を届けたい。
できる限り変換結果を使い回して、無駄な変換を避けたい。そこでLightFile Proxyは、CloudFrontとは別に自前のキャッシュを持っている。CloudFrontのキャッシュは期限が来れば消えるので、そこだけに頼ると、消えるたびに同じ画像を変換し直すことになるからだ。
連載「詳説 LightFile Proxy」
AWS CloudFrontの後ろに置いて、既存のサイトを変えずに画像をWebPで配信する仕組みを説明する連載。
- サイトに手を加えず画像をWebPで配信する仕組み
- 拡張子は.jpgのまま中身をWebPにしてよいのか
- 画像変換のタイミングとその最適解
- 次世代画像フォーマットの採用でどのくらい画像が軽くなるか
- WebP化によるデータ削減で見た目は劣化するか
- 画像のリンク切れを起こさない二重の障害対応
- 無駄な変換を避け高速に画像を配信するためのキャッシュ戦略(本記事)
- その他の細かな特徴(10月2日公開予定)
- AWS純正のDITで代替できるか(10月3日公開予定)
- 料金体系(10月4日公開予定)
キャッシュは二重になっている

CloudFrontのキャッシュは、アクセスの少ない画像から順に押し出されていく。ロングテールを描くECサイトでは、たまにしか見られない画像ほど消えやすい。
LightFile Proxyの変換キャッシュはクラスタのノード全体で共有している。ここには変換したWebPと、変換しないと決めた画像の判断が入っている。CloudFrontから消えた画像をもう一度求められても、ここに当たれば変換は走らない。
どのエッジから来ても変換は1回
CloudFrontのキャッシュは世界中のPOP(エッジロケーション)ごとに持つ。POPで外れると、CloudFrontは最寄りのリージョナルエッジキャッシュへ取りに行き、そこでも外れたときにオリジンへ来る。AWSのドキュメントにはこう書かれている。
This makes sure that all of the POPs in a region share a local cache, eliminating multiple requests to origin servers.
(これにより、同じリージョンのすべてのPOPがローカルなキャッシュを共有し、オリジンサーバーへの複数回のリクエストが起きないようにする)
つまりリージョンの中ではまとまるが、リージョンをまたげばキャッシュは別になる。同じ画像でも、日本からのアクセスと海外からのアクセスでは、オリジンへのリクエストが別々に届く。そのたびに変換していては無駄が出る。
LightFile Proxyの変換キャッシュはクラスタで1つなので、どのエッジ経由で来ても変換は最初の1回だけで済む。2回目以降は、どこから来ても変換済みが返る。
キャッシュのキーはオリジン側のURL
キーにしているのは、エンドユーザーがリクエストしたURLではなく、取りに行くオリジン側のURLになる。
この違いは複数の公開ホストから同じ画像を配信しているサイトで効いてくる。www.example.com と shop.example.com が同じオリジンの同じ画像を指しているなら、キャッシュはひとつで済む。どちらから先にアクセスが来ても、2度目は変換済みが返る。
期限が切れてもHEADひとつで延命する
キャッシュには3つの寿命を設けている。
| 既定値 | 使いどころ | |
|---|---|---|
| ロング | 86,400秒(1日) | 変換済みを返すとき |
| ショート | 10秒 | 変換を待っている間にオリジナルを返すとき |
| エラー | 300秒 | オリジンが2xx以外を返したとき |
ロングの期限内なら、オリジンには問い合わせずに変換済みを返す。期限を過ぎるとキャッシュはstale(古くなった状態)になるが、staleになったからといってすぐ作り直すわけではない。HTTPのキャッシュと同じように再検証(revalidate)する。
- オリジンへ HEAD を送る
- 保存したときの
etagかlast-modifiedと一致すれば、変更なしとみなして期限だけ延ばす - 一致しなければ、オリジナルを返しつつ変換をやり直す
更新のない画像は2で止まるので、変換のコストは1画像につき1回で済む。再検証で投げるのもHEADひとつなので、画像の本体は取りに行かない。

画像を差し替えたときの追随
オリジン側で画像を差し替えると、etag か last-modified が変わる。次に期限が切れたタイミングで3の経路に入り、新しい画像が変換されて置き換わる。
URLを変えたり、手でキャッシュを消したりする必要はない。 商品画像の差し替えが日常的に起きるサイトでも、運用の手順は変わらない。
ただし、差し替えてから切り替わるまでには期限ぶんの間がある。すぐ反映させたいときは、次のキャッシュクリアを使う。
意図的に消すときはクリア
画像を差し替えてすぐ反映させたいときや、間違った画像を配信してしまったときは、キャッシュを消す。この操作をクリアと呼んでいる。
消す前に、消える範囲を見積もれる。 URLの前方一致で条件を指定すると、当たるキャッシュの件数・合計サイズ・実例が返る。この時点では何も消さない。「このディレクトリ以下を消したいが、どれだけ消えるのか」を先に確かめてから、同じ条件で削除に進める。
前方一致はディレクトリの境界で切るので、/shoes/ の指定が /shoes-sale.jpg を巻き込むことはない。
消す順番が決まっている。 まず自分のキャッシュを消し、そのあとCloudFrontへ対応するパスの無効化をリクエストする。逆にすると、CloudFrontが消えた直後にLightFile Proxyの古いキャッシュを取りに行ってしまい、また同じ画像がキャッシュされる。
この2つを、利用者のひとつの操作から続けて実行する。

LightFile Proxy側だけ消してもCloudFrontに古い画像が残っていれば意味がないので、ここまでを一続きにしてある。HTTP APIも用意しているので、入稿のシステムから呼ぶこともできる。
クリアだけをサイト運用者に任せられる
画像の差し替えをすぐ反映させたい場面は、サイトを日々まわしている人のところで起きる。ところがLightFile Proxyの管理画面にはオリジンの設定やCloudFront連携が並んでいる。そこを開いてもらうのは、操作ミスの心配があって気が進まない。
そこでキャッシュクリアだけができる画面を別に用意している。

できるのは、登録されたサイトのキャッシュを消すことだけになる。設定はどこにもなく、触れる範囲はそのサイトの画像に限られる。サインインはGoogle・Microsoftのアカウントか、メールに届く一時リンクで、許可するメールアドレスは個別でもドメイン単位でも指定できる。
日々の反映で毎回インフラの担当者を呼ばなくてよくなる。 複数のサイトを預かっている代理店なら、サイトごとの担当者に消す操作だけを渡せる。
確保した容量に収めるのはパージ
クリアが運用者の意図で消す操作なのに対して、パージは容量へ収めるため自動で走る掃除になる。目的が違う。
キャッシュに使う容量は、クラスタごとに決めて確保する。既定は200GBで、足りなければ追加の費用で増やせる。大きく取るほど、たまにしかアクセスされない画像も残り続けるので、変換のやり直しが減る。
確保した容量を超えたからといって、その場で止めたり消したりはしない。超えた分はひとまず受け入れておき、1日1回まとめて収める。 パージは毎日03:00(JST)に走り、最終アクセス日時の古い順に、確保した容量へ収まるところまで削除する。よく見られている画像は残り、誰も見なくなった画像から消えていく。

増やしている例もある。400GBで確保しているクラスタが466GBまで膨らんでいて、35,513件(42GB)を削除して戻したことがあった。最大のクラスタは約530万件のキャッシュを抱えていて、3TBを確保している。