🌐

CloudFront コンテンツ取得時間のベンチマークを取ってみた

に公開

弊社では AWS を活用しており、ウェブのコンテンツ配信などには CloudFront と呼ばれるサービスを使用しています。

CloudFront には、キャッシュ機能や Lambda@Edge など様々な機能があり、ニーズに合わせて設定を細かく変更することができます。

各設定によって挙動がどう変わるのかに関する記事は多くあるものの、キャッシュや Lambda@Edge の有無によるコンテンツ取得時間(レイテンシー)の違いを比べた記事は調べた限り見つかりません。

おおまかにキャッシュがあれば短くなり、Lambda@Edge が増えれば長くなることは分かるのですが、それが実際にどのくらいの差なのか。ユーザーにどのくらい影響があるのかは分かりませんでした。

そのため、この記事ではキャッシュや Lambda@Edge の有無によって、コンテンツ取得時間が実際にどのくらい変化するのか、ベンチマークを取ってみました!

CloudFront, Lambda@Edge とは

Amazon CloudFront | AWS
CloudFront とは AWS が提供する CDN サービスです。

Lambda@Edge | AWS
Lambda@Edge とは CloudFront の機能で、リージョンと比べてよりユーザーに近い場所(エッジロケーション)でコードを実行できるサービスです。

Lambda@Edge は下図のように Viewer request, Viewer response, Origin request, Origin response の4箇所で使うことができ、ユーザーから CloudFront の間、CloudFront からオリジンの間に処理を挟むことができます。

引用:Lambda@Edge 関数をトリガーできる CloudFront イベント - Amazon CloudFront

コンテンツ取得時間が変わる理由

① CloudFront のキャッシュの有無

CloudFront のエッジキャッシュは、キャッシュポリシー や オリジンから返ってくるレスポンスの Cache-Control, Expires ヘッダーなどを使用して制御することができます。

エッジにキャッシュがない場合・有効期限が切れていた場合は、オリジンまでファイルを取りに行く必要があり、その分、コンテンツ取得時間が伸びます。

② Lambda@Edge の処理

Lambda@Edge を追加した分だけ処理も増えるため、コンテンツ取得時間が伸びます。

ベンチマーク

具体的にどのくらい違いが出るのか、福井県鯖江市にある jig.jp 開発センターからベンチマークを行ってみました。(S3 がある東京リージョンからは地理的に少し距離がある場所)

測定は、Deno のベンチマーク機能を使って、下記の各ファイルに対して fetch API でのコンテンツ取得時間を測定しました。
CloudFront のビヘイビアを使って、各ファイルごとにキャッシュポリシーや Lambda@Edge の設定をしています。

ファイル名 キャッシュ状況 Lambda@Edge
script-hit.js Hit -
script-miss.js Miss -
script-refreshhit.js RefreshHit [1] -
script-le1.js Miss Viewer request
script-le2.js Miss Viewer request
Origin request
script-le3.js Miss Viewer request
Origin request, Origin response
script-le4.js Miss Viewer request, Viewer response
Origin request, Origin response

キャッシュ状況による違い

キャッシュ状況が Hit(script-hit.js)、Miss(script-miss.js)、RefreshHit(script-refreshhit.js)で比較を行いました。

オリジンにアクセスせずにエッジからキャッシュを返す Hit は取得時間が短く他と比べて半分以下になることが分かりました。
さらに、Miss と比べて RefreshHit のほうが良い結果になることも分かりました。(コンテンツのファイルサイズが大きい場合は RefreshHit と Miss の差が更に大きくなると予想されます)

Lambda@Edge による違い

Lambda@Edge は最大4箇所に設定できるため、0~4 箇所と変化させた script-miss.js, script-le1.js ~ script-le4.js で比較を行いました。

各 Lambda@Edge の関数は、以下のように特に何もしないシンプルなものとしています。

Viewer request の Lambda@Edge 関数例
"use strict";

exports.handler = (event, context, callback) => {
  const request = event.Records[0].cf.request;
  callback(null, request);
};

何もしない関数であっても追加するだけで 1関数につき 20 ~ 25 ms ほど取得時間が伸びることが分かりました。
実環境では、関数内で行うさまざまな処理を含むため、もう少し伸びることが予想されます。

まとめ

今回は、CloudFront のキャッシュや Lambda@Edge に注目してコンテンツ取得時間の比較をしてきました。

「キャッシュがあれば短くなり、Lambda@Edge が増えれば長くなる」という理解をベンチマークの結果とともに確認することができました。また、取得時間が最短のもの(キャッシュ Hit & Lambda@Edge なし)と、最長のもの(キャッシュ Miss & Lambda@Edge 4箇所)では、約 5.7 倍もの差があることも分かりました。

実際にはクライアントの環境や扱うファイルによって、結果やユーザーへの影響が違うと思います。今回の結果はあくまでも参考程度に捉えていただけると幸いです。

また、CloudFront Functions と呼ばれる Lambda@Edge よりも制約が厳しい分より高速に動くエッジ関数もありますが、自分の知識が浅くうまく検証ができなかったため今回は割愛させていただいています🙇

今回の検証に使用した CloudFront の構成(AWS CDK)、ベンチマークコード(Deno スクリプト)は下記 GitHub にて公開してますので、ご自身の環境で気になる方は試してみてください!

https://github.com/kamekyame/cf-bench

脚注
  1. RefreshHit とは、CloudFront キャッシュの有効期限は切れていたもののオリジンにあるファイルに変更がなく CloudFront キャッシュが再利用された状態を指します。キャッシュポリシーの最大 TTL を 1ms 以上にした状態でデフォルト TTL を 0ms にすることで常に RefreshHit にしています。 ↩︎

jig.jp Engineers' Blog

Discussion