🛵

Cloudflare Workers の実行時間が遅くなることがある

2024/12/19に公開

はじめに

Morisawa Fonts というフォントサブスクリプションサービスで SRE を担当しています。

https://morisawafonts.com/

先日、Morisawa Fonts で Web フォントのサービス をリリースしました。これはモリサワのフォントを Web サイトで利用できるサービスです。

その中で、Web フォントを配信する機能の一部 を Cloudflare Workers を使って実現しました。以下は簡単な構成図です。

実行時間

Cloudflare Workers のダッシュボードでは、CPU 時間Wall 時間 の 2 種類の実行時間を確認できます。 Wall 時間 とは Worker の呼び出しの開始から終了までの合計時間です。

この Worker では、 CPU 時間は数 ms 程度に収まっていました。

一方で Wall 時間は次のグラフのようになっていました。グラフの色は各パーセンタイルごとの実行時間を示しています。(具体的な時間は伏せています)

  • \textcolor{#fbad41}{■} 50 パーセンタイル
  • \textcolor{#55d584}{■} 75 パーセンタイル
  • \textcolor{#4693ff}{■} 99 パーセンタイル
  • \textcolor{#f85189}{■} 99.9 パーセンタイル

\textcolor{#55d584}{■} 75 パーセンタイルまでは問題ない範囲内でした。ところが、 \textcolor{#4693ff}{■} 99 パーセンタイルや \textcolor{#f85189}{■} 99.9 パーセンタイルでは、極端に遅くなることがわかります。

稀なケースとはいえ、Web フォントの配信が遅いのは好ましくありません。Web フォントのロードに時間がかかると、画面のチラつきなどが発生し、サイト閲覧者の体験に悪影響を与えることになります。

計測する

D1 か R2 のいずれかが原因と考え、それぞれの処理時間を計測しました。

暫定的な対応として、Performance API を使った簡単なコードを追加しました。

https://developers.cloudflare.com/workers/runtime-apis/performance/

const time0 = performance.now();

// ... D1 の処理 ...

const time1 = performance.now();

// ... R2 の処理 ...

const time2 = performance.now();

// TODO あとで消す
// 遅い場合は警告を出す
if (time2 - time0 > 1000) {
  console.warn("too late", {
    time: time2 - time0,
    d1: time1 - time0,
    r2: time2 - time1,
  });
}

1000ms(1 秒)を超える実行時間の場合に警告を出すようにしました。 出力したログはダッシュボードの Workers Logs で確認できます。

https://developers.cloudflare.com/workers/observability/logs/workers-logs/


Workers Logs

ログの確認

ログの一つを展開すると、次のようになっていました。ログには Cloudflare が付加した情報も含まれています。



D1 と R2 のいずれも時間がかかっていることが分かります。

Worker の実行ログ (Invocation Logs) もあわせて確認します。 $metadata.requestId の値を使ってログを検索できます。



Cloudflare が提供する実行ログにはエンドユーザーの地理情報も含まれています。このログはフランスからのアクセスのようです。

colo の値に注目します。これは Worker が実行された Cloudflare のデータセンターの場所を示しています。例えば、このログにある CDG はフランスのパリにあるデータセンターです。

Cloudflare のデータセンターの名前は 3 文字の空港コード ✈️ で表されます。日本国内で実行されているのであれば、 NRT(東京)や KIX(大阪)などになります。 [1]

実行時間の警告が出ている他のものも、同様にログも確認すると colo の値が海外のものになっていました。 [2]

原因の考察

Cloudflare Workers はエッジ、すなわち エンドユーザーに近い場所 で実行されることが、特徴の一つです。

一方、 D1 や R2 の実体(オリジン)は、日本国内ないしはその近傍にあると推測されます。

そのため、海外のデータセンターで Worker が実行される場合、 物理的な距離 が原因で遅延が発生していると考えられます。

アクセスが増えれば、キャッシュやレプリカが Worker の近くに配置され改善が見込めるはずです。ただ、現状では海外からのアクセスが散発的にしかないため、このような問題が発生していると思われます。

対策について

これについては 対策をしない という選択肢もあります。現時点は国内のエンドユーザーがほとんどのサービスであり、このままでも問題ないという判断もできます。

とはいえ、モリサワは日本語だけでなく多言語のフォントを提供しています。

https://www.morisawa.co.jp/fonts/multilingual/

いずれは Web フォントが海外のサイトで利用されることも考えられます。ということで、対策案を考えてみました。

階層型キャッシュ (Tiered Cache)

これは Cloudflare のデータセンター間でキャッシュを階層化し、キャッシュヒット率を向上させる設定です。

https://developers.cloudflare.com/cache/how-to/tiered-cache/

クラスメソッドさんのブログにも解説があります。

https://dev.classmethod.jp/articles/how-cloudflare-tiered-cache-works/

こちらの設定はデメリットが無さそうなので 有効化 しました。

ダッシュボードの Caching から設定できます。 Terraform でも設定できます。

有効にした結果、 R2 へのアクセス時間は 若干改善 しました。(残念ながら完全に解消はしません)

個人的にはデフォルトで有効であって欲しい設定だと思いました。

スマートプレースメント (Smart Placement)

これは Worker をオリジンに近いデータセンターで実行する設定です。

https://developers.cloudflare.com/workers/configuration/smart-placement/

こちらの設定は検討の結果、 保留 としました。

というのも、 Workers とオリジンの距離が近くなったところで、エンドユーザーからの距離が遠くなるので、根本的な解決にならないと考えたためです。また、キャッシュも離れてしまうため、逆に恩恵が失われる可能性もあると考えました。

D1 を Workers KV に移行する

こちらは将来的に実施を検討しています。

Workers KV もどこかにオリジンがあることには変わりません。ただ、KV はキーごとにエッジでキャッシュされるため、海外からのアクセスが多くなれば、キャッシュヒット率の向上が期待できます。

https://developers.cloudflare.com/kv/concepts/how-kv-works/

この Worker では D1 を参照にしか使っていないため、 Workers KV に置き換えることで効率化できそうと考えました。

D1 を採用した理由は、別で行っているデータ更新側の処理で SQL を使いたかったためです。しかし、Web フォントは配信の方が重要なサービスであるため、更新よりも参照を優先する方が適切です。

まとめ

Cloudflare Workers はエンドユーザーに近い場所で実行されます。そのため、Cloudflare で完結する構成であっても、オリジンとの物理的な距離を意識する必要があるということがわかりました。

同様の現象で困っている方々の参考になれば幸いです。

脚注
  1. Cloudflare のデータセンターの一覧は https://www.cloudflarestatus.com/ から確認できます。 ↩︎

  2. ちなみに、日本国内からのアクセスであっても colo が北米、欧州、シンガポールなど海外のデータセンターになるケースがありました。これに関しては制御できないため、どうしようもありません。 ↩︎

モリサワ Tech Blog

Discussion