以下のようなリンクの埋め込み表示を追加したら、CMSに使っているNotion上でページを更新してもサイト上に反映されないことがあった。
Vercelのログを見に行くとNext.jsのサーバサイドレンダリング処理でタイムアウトが発生している。無料版のVercelはサーバレス関数の処理時間に制限がある。(ドキュメントを見ると5秒と書いてあるけど自分の環境では10秒だった。謎)
サーバサイドレンダリングに失敗するとページは更新されない。ただしISRにしているので前回のレンダリング結果である更新前のページが表示される。
原因としては、リンク先からOGP画像のリンクなどを取得する処理でHTTP通信とHTMLのパースに時間がかかっているようだ。リンク先によっては応答が遅い場合があるので、全体の処理を制限時間内に間に合わせるにはそうしたリンクに対する処理は適度なところで切り上げる必要がある。
以下の記事を参考にPromise.race()を使って処理をタイムアウトさせる機構を追加した。
次のような、引数に与えた時間を過ぎたらrejectされるPromiseを返す関数を用意しておく。
async function timeout(msec: number) {
return new Promise((_, reject) => setTimeout(reject, msec))
}
メタタグを取得する処理で、ページにある各リンクにリクエストを送ってパースする処理を非同期で実行している。Promiseの配列を作ってPromise.all()で待っているので、その配列に入れるPromiseをPromise.race()と上の関数を使ってタイムアウトするようにしておく。(startMomentはNext.jsのgetStaticProps()を開始したタイミング。10秒制限から計算して適当に8秒以上経過していたらタイムアウトにする)
const getOgpPromises = []
for (const post of posts) {
const promise = fetch(post.content.link, { redirect: 'follow' })
.then((res) => res.text())
.then((text) => {
const { document } = new JSDOM(text).window;
// メタタグ取得処理
});
getOgpPromises.push(Promise.race([
promise,
timeout(8000 - moment().diff(startMoment))
]));
}
await Promise.all(getOgpPromises);
これにてひとまずタイムアウトはせず、ページが更新されるようになった。