第27回: スケジューラ設計と再実行耐性 — 「2回実行されても大丈夫」な設計になっていますか?

章: 第8章: パフォーマンスと本番運用実践

スケジュールジョブが2回実行されたら、何が起きますか?

定期バッチが何らかの理由で二重起動された場合——同じデータが2回送信される、請求が重複する、集計値がずれる——こうした事故が起きた経験はありませんか?

cronは「必ず1回だけ実行される」保証がありません。ネットワーク障害・サーバー再起動・デプロイのタイミングによって、同じジョブが複数回起動するケースは現実に起こります。idempotent(冪等)な実装重複実行防止がスケジューラ設計の核心です。

問題の本質と解決策

問題: 重複実行を前提としない設計では、2回目の実行が副作用をもたらします。また、複数サーバーでそれぞれがスケジュールを起動すると、さらに複雑な問題が生じます。

解決策: withoutOverlapping() で同じジョブの並行実行を防ぎ、onOneServer() で複数サーバーでも1台のみ実行されるよう制御します。処理そのものも「何度実行しても同じ結果になる」設計にします。

重複実行対策あり・なし 比較

観点 対策なし 対策あり
前回の実行中に次回が起動 並行実行され二重処理が発生 withoutOverlapping() でスキップ
複数サーバーで起動 台数分のジョブが走る onOneServer() で1台に限定
データの冪等性 二重INSERT・二重送信が起きうる 実行済みフラグや upsert で防ぐ
障害時の再試行 手作業で判断 安全に再実行できる

チェックポイント: スケジュールジョブに withoutOverlapping()onOneServer() を付けていますか?どちらか一方だけでは不完全です。キャッシュドライバが database または redis であることも確認しましょう。

実装サンプル


<?php
// app/Console/Kernel.php
protected function schedule(Schedule $schedule): void
{
    $schedule->command('reports:daily')
        ->dailyAt('01:00')
        ->withoutOverlapping()
        ->onOneServer();
}

まとめ & 次のステップ

  • cronは「必ず1回だけ実行される」保証がないため、冪等な設計が前提です
  • withoutOverlapping() で並行実行を防ぎ、onOneServer() で複数サーバーに対応します
  • 処理自体も「何度実行しても同じ結果」になるよう upsert や実行済みフラグで設計します
  • onOneServer() はキャッシュドライバが database または redis である必要があります
  • まずは最も重要なバッチジョブ1つに両メソッドを付けるところから始めましょう

次回は バッチ処理の分割と再開戦略 を学びます。大量データを安全に処理するための分割設計を解説します。

Related Articles