章: 第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つに両メソッドを付けるところから始めましょう
次回は バッチ処理の分割と再開戦略 を学びます。大量データを安全に処理するための分割設計を解説します。