🧊

CDNエッジでSSR、ではなくSSG+αできないか

なんだかんだここ半年くらいずっとCloudflare Workersを触ってます。
で、CDNエッジでコードが動くことはわかった。制約があることも、それなりに速いこともわかった。

で、これをどう扱っていくのが人類にとって良いんかな〜?みたいなことをずっと考えてた折に、VercelやらRemixが「これからはエッジだ!」(意訳)みたいなメッセージを出してて、ふふ〜んってなった今日このごろ。

We believe the modern Web is at the Edge and embraces the open Web platform.
https://vercel.com/blog/vercel-funding-series-d-and-valuation

We leveraged distributed systems at the edge instead of static builds.
https://remix.run/docs/en/v1/guides/philosophy

なのでここで現状を整理して、少しでも自分の思考の足しにしたいなーと。

エッジでSSR

SvelteKit w/ cloudflareアダプターとか、(Next.jsはまだCFWでSSRできないはずなので代わりに)Flareactとか、Remixとかがやろうとしてるやつ。

まあ実行環境だけを見るならば、従来のFaaSでやるよりは速いはずなので、諸々の制限さえ問題にならないなら別にええんではって感じ。

RemixのSSRに全振りする思想もわからんでもないし、エッジのおかげで妥協点のボーダーがいい感じになってきたのはあるかもしれない。RSCにしろ、Streaming SSRにしろ、従来のFaaSでやるよりかはエッジでやったほうが速くて嬉しいはずなので。

ただ、リクエストが実行環境に届くまでは速くなっても、そこから先が引き続きボトルネックになるならば、ユーザー体験としてはさほど変わりないのでは〜?と思ったりはする。

ことCFWに関して言えば、現状HTTPしかプロトコルが使えないので、DBまわりへのアクセスで余計に遅くなったりしないだろうか?データのあるNW内で完結したほうが速かったり?とか。そもそもエッジでSSRしまくるのが実用に足るものなのか?って話も、これから出てくる気はする。そもそもSSR・・話も引き続き。

ただDBもエッジ側によってくるのは順当な流れな気がしていて、CFWにはKVやDOがあるし、MarcrometaとかDBをエッジで!って言ってるサービスもある。

Macrometa - Build Faster Real-Time Applications

まぁセンシティブなデータまでエッジDBに載せる未来はあんまし想像つかんけど。

まぁこのあたりは巷のポエムに任せるとして、Cloudflare Workersだからこそできそうな、その他の使い方はないのか?ってことを考えてるってのが本題。

エッジでSSG+α

Cloudflare Workersには、`HTMLRewriter`っていう知られざるAPIがあって、それをなんとか活用する方法はないか?って考えてた。

https://developers.cloudflare.com/workers/runtime-apis/html-rewriter

これはその名の通りHTMLをリライトして返せるAPI

// これを
const htmlStr = await htmlResp.body();
return new Response(htmlStr.replace(/* ... */));

// こう書ける
return new HTMLRewriter()
  .on("a", anchorRewriter).transform(htmlResp);

というように使える。

既存のHTMLがある前提のAPIになってて、たとえば事前に生成してKVに保存しておいたHTMLを取り出して加工して返すことができる。

jQueryライクなセレクタで対象の要素を見つけて、ピンポイントに書き換えができる。リライトするけど、ちゃんとストリーミングされたレスポンスがクライアントに返るところがポイント。チューニングがんばってるみたいな話も書いてあった。

A History of HTML Parsing at Cloudflare: Part 1
Low Output Latency (LOL)HTML parser/rewriter

なのでこれを使って、

  • SSGで事前生成したHTMLでマーキングしておき、それをKVに保存しておく
  • Workers Sitesでホスティングして、KVから取り出して返す
  • ユーザー情報とか動的な情報を`HTMLRewriter`で埋め込み
  • そのままでいいページはそのまま返す

っていうパターンができる気がしていて、これは実用的か?ってのを考えてる。SSRフレームワークのアプローチの逆方向っぽいやつ。

SSR系のフレームワークを使ってその上でSSGする場合、不要なJSから逃げられないことが多い(サイズも実行も)のが現状なので、SSG系のフレームワークを使って、その上で動的部分を補うアプローチはどうだろうか、と。EleventyにしろAstroにしろ、動的部分はクライアントで処理するしかなくて不利で〜みたいなところをカバーできないかなーと。

  • 完全に静的な部分: SSGしてあるからよい
  • 初期表示でほしい動的な部分: リライトして対応、その上ストリーミングして返せる
  • その後でいい動的な部分: SSGしたときに、(Partial)Hydrationするようにしたまま放置でよい

ってな感じのイメージ。(未検証)

考えられるデメリットは、

  • SSGする層とリライトする層でコードが分散してしまう
    • アーキテクチャとして難易度が高いかも
    • リライトする層でも結局SSRっぽいコードが必要
    • 好きなテンプレートエンジンも使えるけど、サーバーレンダリングしてたあの頃みたい
  • リライトした後のHydrationとかどうすんだ
  • めちゃめちゃベンダーロックイン
    • いやまぁ・・

という感じで、考えてはみたものの、そこまでお手軽ではなさそう。

けど、インタラクティブではないがLCPに大きく関わる・・っていうポイントに絞って使うのであれば、ナシではないかなーとも。まぁ手札の一枚として。

そもそもSSG(+ (Partial)Hydration)する構成を選んだ時点で、LCPやTTIに影響があるような動的部分はSSGによって解消されてる前提があるはず。そういう意味では、後から動的にしたい部分はおとなしくCSRする覚悟や判断があったはず。どうしても描画してから返したいなら、そもそもそれはSSGが不適合なサイトだったってだけ。

というわけで・・、可能性はあった気がしたけど、やはり動的部分がメインのサイトに関しては、

  • SSRする
    • エッジでやれるようになって速くなるならラッキー

もしくは、

  • SSG + (Partial)Hydrationする
    • 妥協点として

ってのが妥当な選択肢なんかなー。

現状のSSRはパフォーマンスに問題があると思うけど、ReactのSSRはそのへんも視野に入ってきてるぽいし。Svelteはまだそういう気配ないけど。そういえば既にStreaming SSRできるMarkoは、CFWで動くんやろうか?

New Suspense SSR Architecture in React 18 · Discussion #37 · reactwg/react-18 · GitHub
Async/streaming SSR renderer · Issue #958 · sveltejs/svelte · GitHub

ってもすぐには実用的なレベルにならんやろうし、Reactはそれ自体が相変わらず難しいし、SSR自体の技術的難易度が高いことにも変わりはないし、クライアントに落ちるJSのサイズも極力減らしたいし・・。

やっぱエッジでSSG+αもとい、PartialSSRなこの路線、もう少し探求してみるべき・・・?いやでも・・、みたいなことを悶々と考えてるという話でした。