Closed8

Next.jsのISRをGAE + CDNで試す実験

Next.jsのISRをVercel以外でやろうとするとなかなかしんどそう。

https://zenn.dev/catnose99/scraps/f1c9a98c5651f1

そこで、Next.jsにデフォルトで用意されているISR(getStaticPropsによるもの)は使わずに、ISRと同じ挙動を「SSR + CDN」で実現できないか試してみる会。

今回の実験では、Next.jsを動かすサーバーとしてGoogle App Engine(GAE)を使ってみる。

失敗:Next.jsのISRをとりあえずGAEで動かしてみる

pages/[isr].js
export default function Page(props) {
  return (<p>現在時刻は{props.currentTime}です。</p>);
}

export async function getStaticProps() {
  const date = new Date();
  const currentTime = date.toLocaleString();
  return {
    props: { currentTime },
    revalidate: 10,
  };
}

export const getStaticPaths = async () => {
  return {
    paths: [],
    fallback: "blocking",
  };
};

結果

GAEのログを見ると以下のようなエラーが発生している。

A 2021-03-15T09:34:03.960288Z Failed to update prerender files for /isr [Error: EROFS: read-only file system, open '/workspace/.next/server/pages/isr.html'] { 
A 2021-03-15T09:34:03.960312Z   errno: -30, 
A 2021-03-15T09:34:03.960319Z   code: 'EROFS', 
A 2021-03-15T09:34:03.960325Z   syscall: 'open', 
A 2021-03-15T09:34:03.960331Z   path: '/workspace/.next/server/pages/isr.html' 
A 2021-03-15T09:34:03.960336Z } 

Next.jsのISRはファイルシステムに書き込む仕組みになっているからか(まだ実装部分を読んでおらずよく分かっていない)エラーが出る模様。

仮に書き込みができたとしても複数のインスタンス間でキャッシュが共有するのが難しそう。

GAE + stale-while-revalidateに対応したCDNを試す

ここからが本題。Next.jsのISRを使わずにISRと同じ挙動(stale-while-revalidate)を実現できないか試みる。stale-while-revalidateについては以下の記事が分かりやすい。

https://blog.jxck.io/entries/2016-04-16/stale-while-revalidate.html

具体的には以下のことを試す。

  • Next.jsではgetServerSidePropsを使ってSSRをする。このときCache-Controlヘッダにstale-while-revalidateを含めるようにする。
  • Next.jsの前にstale-while-revalidateヘッダに対応したCDNを配置する

これでNext.jsのISRと同じ挙動になるかを調べる。Next.jsは単純にSSRをするだけなので、他のフレームワークを使っても同じことができるはず。

CDNサービス

2021年2月時点でstale-while-revalidateに対応したCDNサービスは例えば以下。

  • Fastly
  • CloudCDN
  • Cloudflare

ただ、Cloudflareは料金表を見ると「キャッシュ最小TTL有効期限」が$200 / 月のプランでも30秒以上になってるのが気になる。この制限がmax-ageの最小時間に該当する場合、ISR的なことをやろうとしても30秒以上は再検証されないことになる

GAE + CloudCDNで試してみた

getServerSidePropsの中でレスポンスヘッダのCache-Controlに以下の値をセットする

public, max-age=10, stale-while-revalidate=86400"

これで10秒間はキャッシュ。それ以降はデータの再取得が行われるまでは一旦古いキャッシュを返す。1日(86400秒)以上経つと古いキャッシュも削除される。

デプロイして試してみたところ成功した。以下のようにISRっぽい動きになった。

  • 初回リクエストでは最新のデータが表示
  • 10秒間は何度リロードしても初回のキャッシュが表示される
  • 5分後にアクセスしても初回キャッシュが表示される。リロードすると最新のデータに書き換わる

GAE + CDNにすると[GAEのエッジキャッシュ]と[CDNのキャッシュ]の両方が効いてしまう(?)

実際にGAEでNext.jsを動かしてみて思い出したのだが、GAEにはデフォルトでエッジキャッシュが備わっている。Cache-Controlヘッダが指定されていると、GAEの方でもレスポンスがキャッシュされることになる(?)

追記:CloudCDNと連携した場合GAEデフォルトのエッジキャッシュは効かない模様 🙆‍♀️
検証してみたところCloudCDNをGAEにつなぐとGAEインスタンス ⇔ CloudCDNとなる。
ただし、CloudflareなどのCDNサービスを使う場合はどうなのか不明

このスクラップは2021/05/07にクローズされました
ログインするとコメントできます