Vercel Workflows 導入時のコスト算定と移行実務
サーバーレスタイムアウトの壁と月額費用の計算
サーバーレス関数でSaaSを運営する際、10秒から60秒というタイムアウト制限は想像以上に足かせとなる。大容量データのバックアップやマルチモーダルAIメディア生成パイプラインを実装する際、従来のサーバーレス環境は頻繁にコードを中断させる。
Vercel Workflowsは、関数が待機状態に入る際にリソース消費を0に下げながら処理を維持する。費用予測のためにTypeScriptスクリプトを直接実行し、月額支出を事前に計算する。1日の実行件数、ステップ数、実行時間、ペイロードサイズをオブジェクトとして定義し、コスト計算関数に投入する。Hobbyプランの月5万イベントと1GB書き込みの制限を超える場合は、Proプランへのアップグレードを事前に準備する必要がある。月1万5000回未満の実行であれば、Proプランの基本クレジット内でまかなえるため、自己ホスト型Redisインフラを維持するよりもコストを削減できる。
`typescript
interface WorkflowMetrics {
dailyExecutions: number;
stepsPerExecution: number;
avgStepDurationMs: number;
payloadSizeKb: number;
}
function calculateEstimatedCost(metrics: WorkflowMetrics): { totalEvents: number; estimatedCostUsd: number } {
const totalEvents = metrics.dailyExecutions * metrics.stepsPerExecution * 30;
const freeTierEvents = 50000;
const overageEvents = Math.max(0, totalEvents - freeTierEvents);
const costPerThousand = 0.20;
const estimatedCostUsd = (overageEvents / 1000) * costPerThungk?: number;
return {
totalEvents,
estimatedCostUsd: Number(((overageEvents / 1000) * costPerThousand).toFixed(2))
};
}
`
メッセージキューからワークフローSDKへの移行手順
BullMQやAWS SQSを使用していた時は、キューの作成、ジョブの発行、受信、Redis接続の管理をすべて自前で構築していた。この過程でコードが不要に肥大化する。Vercel Workflows SDKは、コードレベルのディレクティブによってこのインフラコードを削除する。
移行は3ステップで完了する。第一に、next.config.tsファイルにwithWorkflowラッパーを適用する。第二に、従来のワーカープロセスの代わりにuse stepディレクティブを使用して個別のタスクを非同期関数として記述する。第三に、複雑なParent-Child Jobの連鎖構造を廃止し、use workflow内部でJavaScriptの制御構文とawait sleep関数を使用する。外部Redisへの接続コードがなくなることで、アプリのメモリフットプリントが縮小し、移行時間が短縮される。
`typescript
import { workflow } from "@vercel/workflows";
export const { POST } = workflow(async (context) => {
const userId = context.request.payload.userId;
const data = await context.run("fetch-user-data", async () => {
const res = await fetch(https://api.example.com/users/${userId});
return res.json();
});
await context.sleep("wait-for-processing", "10m");
await context.run("finalize-task", async () => {
await db.users.update({ where: { id: userId }, data: { status: "processed" } });
});
});
`
ネットワーク切断状況下でデータの整合性を守るエラーハンドリング
非決定的障害が発生した際のデータ損失を防ぐためには、エラーの性質をまず切り分ける必要がある。Vercel Workflowsは、一時的エラーと致命的エラーを分けて処理する。
ステップ内でエラーをキャッチするには、まずHTTP 429や500系のエラーが発生した際にRetryableErrorをスローし、指数バックオフに基づくリトライを誘導する。そして、データベースを更新するステップ内部にはユニークキーのチェックロジックを組み込み、リトライ時に重複データが蓄積されるのを防ぐ。最後に、Vercel Log DrainsをSentryと連携させ、構造化されたJSONログをリアルタイムでストリーミングする。このパターンを適用すれば、例外状況下でも中断ポイントから状態が復元され、手動でのデータ復旧作業が不要になる。
`typescript
import { RetryableError } from "@vercel/workflows";
export async function processPaymentStep(context: any, paymentData: any) {
return await context.run("charge-payment", async () => {
const exists = await db.transactions.findUnique({
where: { idempotencyKey: paymentData.key }
});
if (exists) {
return exists;
}
const response = await paymentGateway.charge(paymentData);
if (response.status === 429 || response.status >= 500) {
throw new RetryableError("Payment gateway overloaded, retrying...");
}
return await db.transactions.create({
data: { idempotencyKey: paymentData.key, amount: response.amount }
});
});
}
`