Vercel の Serverless Function がタイムアウトする
Vercel Function は実行時間に上限がある。
Hobby は 10 秒、Pro は 60 秒(最大 300 秒まで `maxDuration` で延長)、Edge Runtime は 25 秒。
`maxDuration` 設定、`Promise.all` での並列化、ジョブキューへの退避、ストリーミングで切断回避の 4 系統で解決する。
公開:
要約
Vercel Function のタイムアウト上限はプランと runtime で決まっている。
Hobby Serverless 10 秒 / Pro Serverless 60 秒(maxDuration で最大 300 秒)/ Edge 25 秒、が出発点。長すぎる処理は maxDuration で延ばす、Promise.all で並列化する、ジョブキューに退避する、ストリーミングで切断を防ぐ、の 4 系統で解決する。
よくある原因
- Hobby の 10 秒上限: 何も指定しなければ Serverless Function は Hobby で 10 秒、Pro でも既定 15 秒で打ち切られる。
maxDuration未指定: Pro / Enterprise ではコード側でmaxDurationを指定しないと既定値のままで、想定より早く切られる。- 外部 API への直列呼び出し: 3 API × 4 秒の直列 fetch を書いていて、簡単に 10 秒を超える。
- Edge Runtime のストリーミング上限: Edge Runtime は 25 秒で接続を切られる仕様。
長時間タスクには不向き。 - キュー退避をしていない: メール送信・PDF 生成・LLM 呼び出しなどの重い処理を 1 リクエスト内で完結させようとしている。
解決策
1. maxDuration を route で指定する
// app/api/heavy/route.ts (Pro plan)
export const maxDuration = 60;
export async function GET() {
const data = await heavyJob();
return Response.json(data);
}公式の Configuring Functions / Duration のとおり、Pro 以上は最大 300 秒まで設定可能。
Hobby では maxDuration を書いても上限は 10 秒のまま。
2. 並列化して時間を縮める
const [a, b, c] = await Promise.all([fetchA(), fetchB(), fetchC()]);3 つの直列 fetch(各 4 秒)が並列化で 4 秒前後に縮む。
fetch 同士に依存関係が無いケースでは最初に検討する。
3. Edge Runtime と Node Serverless の選び方
公式の Runtimes のとおり、軽量 API(認証、レスポンス整形、低レイテンシ)は Edge(コールドスタートほぼゼロ)、Node API 必須 / 長時間 / 重い処理は Node Serverless が原則。
Edge ではストリーミングで進捗を返し続ければ 25 秒上限内なら維持できる。
4. ジョブキューへ退避
長時間処理は Function 内で完結させず、QStash / Inngest / Upstash Queue 等の外部キューに enqueue だけして即 return する。
クライアントは別エンドポイントで進捗をポーリングする構成にする。