😥

CloudflareでPrismaを使うとサーバーからデータを取得するのに時間がかかってしまう

2024/11/24に公開

これは以下のZennのScrapsに書いた開発記録のまとめです。

背景

Remixで作ったアプリをCloudflare Pagesにデプロイしたところ最初のサーバー応答時間(TTFB)が3,800 ~ 5,000msかかってしまった。
このページでは3つのテーブルから計60個ほどのアイテムを取得していた。

以下は実際のLighthouseでの計測結果。

GoogleではTTFBを800ms以下にすることを推奨していることからも、かなり遅いということがわかる。

技術スタック

  • Remix v2.13
  • Cloudflare Pages
  • shadcn/ui
  • Prisma
  • Turso(DB)

原因を探す

https://github.com/saneatsu/remix-on-cf-pages

Remix + Cloudflare Pagesのリポジトリを新たに作って、3つのページを用意し、それぞれのページで3回計測を行った。

URL 概要 TTFB
/json-placeholder-200 JSON PlaceholderからTodoを200件取得 240 ~ 430 ms
/json-placeholder-1000 JSON PlaceholderからTodoを1,000件取得 500 ~ 600 ms
/db-connect DBの2つのテーブルから5件ずつ、計10件取得 1,970 ~ 2,310 ms

DBに接続しているページで明らかに遅いので原因を考えてみる。

  • Remixの書き方がなにかミスっている?
    • /json-placeholder-200/json-placeholder-1000の結果は問題なさそう
  • Cloudflareの設定ミス?
    • → 新たにアプリケーションを作成した直後だし問題ない気がするけど...
  • TursoのCold startが原因?
    • → 月9ドルの有料プランに入ってCold startをなくしてみたけど計測結果変化しなかった

原因わからん。詰んだ/(^o^)\

人に聞こう

偶然にも「Remix Tokyo x Cloudflare Meetup」というものが原因を探していた次の週に開催されて10分ほどプレゼンする予定だったので相談会にさせてもらいました🙇🏻‍♂️🙇🏻‍♂️🙇🏻‍♂️(アドバイスをくださった方ありがとうございました!)

https://x.com/__saneatsu/status/1855873587896627294

結論

Prismaが原因だった。
エッジで動かすならサイズの小さいDrizzleを使おう。

  • エッジで動作させている以上基本的にサイズが大きければ大きいほど遅くなる
    • Prismaは850KiBある。Drizzleは67KiB
  • Cloudflareは長時間アクセスされない場合は落ちる仕様で、Cloudflareの設定で制御できるものではない
  • Prismaは起動が遅い
    • 現在改善中とのこと

https://x.com/jonbharrell/status/1859002511476355313

これは残念ながら事実だ。 しかし、私たちはこの1年間、パフォーマンスを向上させるために懸命に取り組み、成功させた。 来年はさらに努力するつもりであり、私たちが取り組んでいることを共有するのが待ちきれない。

https://x.com/kenn/status/1858981362470187339

Drizzleを導入して再計測

TTFBの計測

PrismaからDrizzleへ移行して(commit) 再計測を行った結果が以下。
無事早くなりました。

ORM URL TTFB
Prisma /db-connect 1,970 ~ 2,310 ms
Drizzle /db-connect 1,020 ~ 1,210 ms

サイズの計測

ついでに以下のコマンドでアプリケーションのサイズも測ってみます。

npx wrangler deploy 'functions/[[path]].ts' --outdir bundled/ --dry-run
アプリ サイズ(gzip)
アプリ作成直後 350 KiB
Prisma追加時 1202.67 KiB
Drizzle追加時 417.39 KiB

つまり、 Prismaが852 KiB (= 1202 - 350) 、Drizzleが67 KiB (= 417 - 350)。PrismaはDrizzleの12.7倍も大きいという結果になりました。

また、Prisma追加時にはCloudflare Workersの制限である1MBを超えているため有料プランに月5ドルで加入する必要がありました。しかし今回Drizzleを導入することで無料プランですませることができました 👏🏻

追記:執筆時の2024-11-24段階では無料プランの上限が1MBでしたが、2024-11-27より上限が3MBになったのでここは取り消します(ただし小さと速いので小ささは正義!)。

mizchiさんのスライドにも1MBをパフォーマンスバジェットにと書いてある。サイズ大事。

https://speakerdeck.com/mizchi/server-side-javascript-notamenobandoruzui-shi-hua?slide=25

おまけ

Meetupでは「Cloudflare Workersの方が機能が多いので基本そっちによせるほうが良い」との声もあったので今回のテスト用リポジトリもCloudflare Workers版を作成してみました。

これを元に自分のアプリもCloudflare Workersにしてみよう。

https://github.com/saneatsu/remix-on-cf-worker

Discussion