🔥

VercelがPPRをNodeランタイムにした件からWebフロントエンドとエッジの動向に迫る

2024/06/15に公開1

こんにちは。sumirenです。

イントロダクション

先日、Twitterで以下のポストが話題になっていました。

https://x.com/leeerob/status/1780705942734331983

一部曖昧なところもありますが、推測と動作検証結果も交えて要約すると、以下のことを言っています。

  • VercelがホスティングしているWebサイトは、エッジでのサーバーサイド処理を行うことをやめた
  • Next.jsに関して、VercelでPPR(Partial Prerendering)を利用した場合、サーバーサイド処理はNodeランタイムで行われる

この記事では、このポストに絡めて、PPRとエッジに関する筆者なりの所感や解説を述べていきます。この記事の大部分はポエムですが、一部、技術的な事実についても述べるつもりです。そうした部分について誤りがあればご教示ください。

Vercelがエッジでのサーバーサイド処理を行うことをやめた件について

エッジの効能

そもそもエッジに期待していたことはなんだったのでしょうか。このポストの最初に述べられているとおり、エッジという言葉は曖昧というか多義的です(というか、Vercelは主犯格なのでお前が言うなという感じですが)。

What is edge, anyway? We have too many products with Edge 😅

実は筆者も、1年以上前から同じような言葉の意味の揺れを気にしていました。以下のポストの通り、筆者は近年の「エッジ」には2つの意味があると考えています。1つはエッジロケーション、もう1つはエッジランタイムです。

https://x.com/sumiren_t/status/1654888615724580864

エッジロケーションとは、昔からエッジという言葉が持っている意味合いで、ユーザーに近い位置でレスポンスを返せるよう、多くの場所に配置されたデータセンターに静的ファイルなどをレプリケートすることを指します。

近年では、このエッジロケーションでプログラムを動かし、レスポンスやルーティングを加工する技術が登場しています。Cloudflare WorkersやCloudFront Functionsなどは、そうした処理をJavaScriptで書くことが可能です。

こうした処理は多くのエッジロケーションにレプリケートされるため、常時サーバーを立てておくことは現実的ではありません。そのため、V8 Isolateのように、Node.jsのサブセットとなるAPIや機能のみをサポートする代わりに、高速で立ち上がるランタイムが開発されました。これがエッジランタイムです。

サーバレスWebフロントエンドとエッジ

サーバレスでWebページを配信するVercelが近年推し進めていたトレンドは、こうした2つの利点をフル活用するものでした。エッジロケーションとエッジランタイムという2つの利点により、ユーザーからのリクエストに対してSSRをエッジで動かせば、よりユーザーに近い位置で、より高速な起動で処理できます。

If I constrain my site to use this limited runtime, and make the tradeoff for access to the Node.js package ecosystem, I can (hypothetically) get better performance.

前提として、ここでいうSSRとは、従来のSSRです。つまり、サーバーサイドでのHTMLの生成(レンダリング)とデータフェッチがセットで行われます。

さて、今回のポストは、これが結果的には上手くいかなかったという主張です。

まず、エッジロケーションであるということに関しては、結局のところデータがオリジンにある点で、ラウンドトリップが悪化すると主張しています。

This hasn't worked for two reasons. First, and most obvious, is that your compute needs to be close to your database. Most data is not globally replicated. So running compute in many regions, which all connect to a us-east database, made no sense. I believe many folks understood this.

また、エッジランタイムという利点についても、データベースの近くでのみ実行する場合でも、Node.jsランタイムのVercel Functionsのほうが起動が速かったという結果が出たようです。

But then, maybe this limited runtime was better even if I only run it close to my database? Well, that was wrong too. We tested it extensively with
@v0. Using the Node.js runtime with 1 vCPU (our "Standard" performance) option for Vercel Functions had faster startups 🤯

筆者の所感としては、正直ポジショントークしがちなVercelのことを信じていないので、特に後者についてはフラットに見ています。なぜNode.jsランタイムのVercel Functionsのほうが、エッジランタイムより速いのか、根拠が示されていません。

一方で、前者については、Cloudflareの動向とも一致しており、データに近い位置でサーバサイド処理をするほうがパフォーマンス上望ましいというのは信憑性が高いと感じます。

Cloudflare WorkersのSmart Placementに見る今後の動向

2023年5月、Cloudflare Workersに新たに「Smart Placement」というフィーチャーが追加されました。これは、Cloudflare Workersをバックエンドのロケーションも考慮して最適な場所に移動させることで、トータルでのパフォーマンスを向上させるものです。

https://blog.cloudflare.com/announcing-workers-smart-placement

これはまさに、Vercelサイドのエッジロケーションでデータフェッチを含むSSRを動かす問題点の主張と同じ懸念を解決するものです。

筆者の知識が正しければ、VercelのEdge FunctionsはCloudflare Workersの上に実装されている認識です(出典がないため、これは誤りである可能性もあります)。そのため、もしSmart Placementが安定版として提供されれば、VercelのEdge Functionsにも、エッジランタイムを活かしつつ、データの近くで処理を行うことができるようになるかもしれません。

そのときにエッジランタイムの恩恵を受けるために非エッジロケーションで実行されるEdge Functionsへの揺り戻しが発生するか、Serverless Functions推しが維持されるかは、Vercelのもう一つの主張である「データベースの近くで実行するならNodeランタイムのほうがエッジランタイムより速い」という主張が真実かどうかで決まるのでしょう。

VercelでNext.jsのPPR(Partial Prerendering)を利用した場合、サーバーサイド処理はNodeランタイムで行われる件について

ここまで、VercelがホスティングしているWebサイトがエッジでのサーバーサイド処理を行うことをやめたこととその根拠について所感を述べてきました。

ここからは、Next.jsのPPR(Partial Prerendering)に関する部分について述べていきます。

PPRの概要

ここまでの議論は、SSR(サーバーサイドレンダリング)がデータフェッチとレンダリングをセットで行うものであるという前提に基づいていました。Vercelの意思決定に従うならば、Next.jsのPages Routerでデータフェッチを含むgetServerSidePropsを書く際、そのルートのランタイムには'edge'ではなく'nodejs'を指定することが推奨されるということになります。

それでは、Next.js App Routerで次世代のレンダリングアーキテクチャであるPPRを利用する場合はどうでしょうか。

PPRでは、ルートごとに静的な部分(データフェッチ等のリクエスト内容に依存しない部分)はビルド時にレンダリングされ、動的な部分はリクエスト毎にデータフェッチ後にレンダリングされます。Streamingと組み合わせることで、静的な部分はリクエストを受け付けた瞬間にレスポンスし、動的な部分はフェッチを待ってレンダリングして追加でレスポンスできます。

これにより、SG(Static Generation)に近い速度でのFCP(First Contentful Paint)と、サーバーサイドフェッチのLCP(Largest Contentful Paint)メリットを両立させることができます。

PPRの詳細については、@akfm_satoさんの以下の記事が参考になります。

https://zenn.dev/akfm/articles/nextjs-partial-pre-rendering

PPRとエッジ

PPRとStreamingがロケーションの議論に大きな影響を与えるのは、PPRは可動域が広く、サーバレスにおいて、静的部分のレスポンスと動的処理をそれぞれ別のランタイムおよびロケーションで動かすことができるからです。

まずリクエストが来たときに、事前にビルドした静的な部分だけをエッジからレスポンスできます。これは完全に静的なので、そもそもエッジランタイムも動いておらず、CDNの処理の範疇で行われるのかもしれません。その後、データフェッチや動的部分のレンダリングは、オリジンロケーションのNodeランタイム(つまりServerless Functions)で行うことができます。

The summary of PPR is: user makes a request, the edge region starts streaming back the HTML for the fast initial visual, and then your compute still runs in us-east or wherever close to your data.

このように、PPRのアーキテクチャにより、エッジとオリジンの両方の利点を活かして、最適化されたパフォーマンスをサーバレスで実現できます。

実際のところ、現状(2024年6月時点)のNext.jsとVercelでは、PPRを指定した場合には自動的にこの構成になるようです。静的な部分をオリジンからレスポンスしたり、動的な部分をエッジで動かしたりすることができません。export const runtime = ...すると、Partial Rendering自体が無効化されます。

Partial Prerenderingが適用されたルートへのアクセス時、Vercel上でのログからServerless Functionsで処理が動いていることが分かります。強い根拠ではありませんが、/_next/postponed/resume/...にサーバーサイド処理の内容が保存されている点からも、エッジで一旦チャンクを返してから遅れてオリジンロケーションでサーバーサイド処理が実行されるような雰囲気を感じられます。

Vercel上のログの画像

とはいえ、筆者の所感としては、動的処理をどこで動かすかは指定したいです。そもそもバックエンドがCloudflare Workersといった場合もありえますし、そうした場合にはVercelレイヤで一旦オリジンまでいかず、とっととEdge FunctionsからCloudflare WorkersにHTTPリクエストしてもらい、あとはWorkers側でそれをエッジで受けるなりSmart Placementでオリジンの近くで受けるなりしたいです。現状、PPRの動的処理がServerless Functionsに固定されていることは残念です。

まとめ

今回の議論では、VercelのVPoPの方のポストに絡めて、エッジとWebフロントエンドの動向について、解説と筆者の所感をポエムとして述べてきました。

CloudflareのSmart Placementの動向を見ても、データフェッチなどサーバーサイドでの処理はデータベースやAPIといったバックエンドリソースの近くで処理を行うことが今後のトレンドとなるように思います。

一方、Vercelのいう「データベースの近くで動かすならNode.jsランタイムがエッジランタイムよりも起動が速い」という主張は、筆者はあまり納得していません。Serverless Functionsがデータやバックエンドのあるオリジンと同じネットワークで動くようピアリング等が行われる前提なら、まだ信憑性があるのですが。

こうしたエッジランタイムおよびエッジロケーションの議論は、PPR(Partial Prerendering)とも密接に関係しています。PPRは可動域が広く、静的部分と動的部分をそれぞれ別のランタイムやロケーションで処理することで、エッジとオリジンの両方の利点を活かした最適化されたパフォーマンスを実現できるためです。こうしたアーキテクチャを共通的な形で支えるためにVercel以外のCDNベンダーも頭を悩ませているのだろうなと思いますが、それはまた別の機会に記事にするかもしれません。

筆者は、Cloudflare WorkersのSmart Placementが安定化すれば、サーバーサイド処理もEdge Functionsに寄せて、ランタイムを全てエッジに統一した方が速くなるのではないかと考えています。少なくとも、そのほうが面白そうです。

よければTwiterもフォローください。

Discussion