LaravelのQueue 設計は失敗と再実行まで含める
Queue 設計で迷う場面は、Laravel実務ではよく出てきます。通知送信、外部API連携、CSV取込、集計処理などです。処理をQueueに投げればレスポンスは速くなります。しかし、失敗時の再実行や重複実行を考えないまま非同期化すると、あとで障害調査が難しくなります。
結論から言うと、Queueは「重い処理を後ろに逃がす仕組み」だけではありません。そのため、LaravelのQueue 設計では、Jobの責務、retry、timeout、failed jobs、冪等性、監視をセットで決めます。
この記事では、LaravelのQueue 設計を実務で扱うときの判断基準を整理します。また、レビューで見られやすい失敗例と改善ポイントも扱います。
Queue 設計で向いている処理を見極める
まず、Queue 設計では、Queueに入れるべき処理を見極めます。Laravelでは公式ドキュメントのQueuesで、Job、Worker、failed jobs、retryなどの仕組みが整理されています。
ただし、仕組みがあるからといって何でもQueue化するのは危険です。ユーザーに結果をすぐ返す必要がある処理、トランザクション内で完結すべき処理、順序保証が強く必要な処理は、同期処理のままにした方が安全な場合があります。
| 処理 | Queue向きか | 判断理由 |
|---|---|---|
| メール送信 | 向いている | 多少遅れてもよく、失敗時に再実行しやすい |
| 外部APIへの通知 | 条件付きで向いている | リトライと冪等性が設計できるなら有効 |
| 注文確定のDB更新 | 慎重に判断 | ユーザーへの結果表示や整合性に直結する |
| CSV取込後の集計 | 向いている | 時間がかかり、進捗管理や再実行と相性がよい |
つまり、Queue 設計では「遅らせてもよいか」だけでは足りません。「失敗しても安全に再実行できるか」を見る必要があります。
Queue 設計で最初に決めるJobの責務
Jobは小さければよい、という話ではありません。一方で、何でも詰め込んだ巨大Jobは、失敗理由が追いにくくなります。
例えば、注文完了後に「メール送信」「在庫連携」「外部CRM通知」「集計更新」を1つのJobにまとめるとします。この場合、CRM通知だけが失敗しても、メール送信まで再実行される恐れがあります。
そこで、再実行単位に合わせてJobを分けます。失敗時に同じ単位でやり直したい処理を1つのJobにする、という考え方です。
// 役割が大きすぎる例
class HandleOrderCompleted implements ShouldQueue
{
public function handle(): void
{
$this->sendMail();
$this->notifyCrm();
$this->updateSummary();
}
}
// 再実行単位に分ける例
SendOrderCompletedMail::dispatch($orderId);
NotifyOrderToCrm::dispatch($orderId);
UpdateOrderSummary::dispatch($orderId);
ただし、分けすぎるとJob間の依存関係が複雑になります。そのため、レビューでは「失敗時にどこからやり直すか」「順序が必要か」「同じデータを何度も読んでいないか」を確認します。
Queue 設計ではretryとtimeoutを処理の性質から決める
Laravel Queueでは、Jobごとに試行回数やタイムアウトを設定できます。Queue 設計で詳しい設定を見るなら、Laravel公式のmax job attempts and timeoutが参考になります。
まず避けたいのは、全Jobに同じretry回数を設定することです。例えば、メール送信の一時的な失敗と、データ不整合による失敗では、再実行すべきかどうかが違います。
| 失敗の種類 | retry方針 | 理由 |
|---|---|---|
| 外部APIの一時的なタイムアウト | 数回リトライする | 時間を置けば成功する可能性がある |
| 認証情報の設定ミス | 短く失敗させる | リトライしても成功しにくい |
| 入力データの不備 | 原則リトライしない | 同じデータでは同じエラーになる |
| DBデッドロック | 限定的にリトライする | 競合が解消すれば成功する場合がある |
また、timeoutは長くすれば安全になるわけではありません。長すぎるtimeoutはWorkerを詰まらせます。逆に短すぎるtimeoutは正常な処理まで失敗扱いにします。
そのため、通常時の処理時間、外部APIのタイムアウト、DBロック待ち、Worker数を見て決めます。さらに、失敗時に通知や監視へつなげる設計も必要です。
Queue 設計では冪等性がないJobをリトライしない
Queue 設計で特に重要なのが冪等性です。冪等性とは、同じ処理が複数回実行されても結果が壊れない性質です。
例えば、ポイント付与Jobが失敗後にリトライされたとします。すでにポイントを付与した後で通信エラーが起きていた場合、再実行で二重付与になるかもしれません。
そこで、Jobの入口で処理済みかどうかを確認します。また、外部APIへ送る場合は、idempotency keyや一意なリクエストIDを使えるか確認します。
class GrantOrderPoint implements ShouldQueue
{
public function handle(): void
{
if (PointHistory::where('order_id', $this->orderId)->exists()) {
return;
}
DB::transaction(function () {
PointHistory::create([
'order_id' => $this->orderId,
'reason' => 'order_completed',
]);
// ポイント残高の更新
});
}
}
もちろん、この例だけで十分とは限りません。同時に同じJobが走る可能性があるなら、DB制約やロックも検討します。つまり、アプリケーション側のチェックだけに頼らないことが重要です。
Queue 設計ではfailed jobsを運用で戻せる形にする
Queueは失敗します。そのため、Queue 設計にはfailed jobsの扱いも含めます。Laravelのfailed jobs機能は便利ですが、ただ保存しているだけでは運用できません。
まず、失敗したJobが何の業務データに紐づくのかを追えるようにします。order_id、user_id、external_request_idなど、調査に必要な識別子をログやJob payloadから追える状態にします。
さらに、手動再実行してよいJobか、データ修正後でないと再実行してはいけないJobかを分けます。ここが曖昧だと、障害対応時に「とりあえずretry」して二次被害を起こします。
- 失敗理由が一時的か恒久的かを分類する
- 再実行前に確認すべきDB状態を決める
- 再実行しても二重処理にならない設計にする
- 失敗が一定件数を超えたら通知する
- 失敗Jobを放置しない運用担当を決める
なお、Queueの監視にはLaravel Horizonも選択肢になります。Redis Queueを使う場合は、待ち行列、処理量、失敗状況を見える化しやすくなります。
Queue 設計ではバッチ処理とスケジューラの境界も決める
LaravelではQueueだけでなく、Task Schedulingもよく使います。バッチ処理では、この2つの役割分担が曖昧になりがちです。
例えば、毎日夜間に対象データを抽出し、1件ずつ外部APIへ送る処理があるとします。この場合、スケジューラは「対象を探してJobを投入する役割」に寄せます。一方で、Queue Jobは「1件分を安全に処理する役割」に寄せると分かりやすくなります。
その結果、途中で一部だけ失敗しても、失敗した単位だけ再実行しやすくなります。また、処理量が増えた場合もWorker数やQueue分割で調整できます。
Queue 設計のレビュー観点
実務レビューでは、コードが動くかだけでなく、失敗時に運用できるかを見ます。特に、外部API連携や金額、在庫、通知に関わるJobでは重要です。
- Jobの責務が再実行単位と合っているか
- retry回数とtimeoutが処理の性質に合っているか
- 同じJobが複数回実行されても壊れないか
- DB制約や一意キーで二重処理を防げているか
- 失敗時に業務IDから調査できるか
- 手動再実行してよい条件が明確か
- Worker停止やQueue滞留を検知できるか
- スケジューラとQueue Jobの責務が混ざっていないか
また、Queueは本番運用で差が出る領域です。ローカルでは問題なく見えても、処理量、外部APIの遅延、Worker数、メモリ使用量によって挙動が変わります。そのため、設計段階で監視と復旧手順まで確認します。
まとめ:Queue 設計は失敗後の復旧まで決める
Queue 設計では、非同期化そのものよりも、失敗した後に安全に戻せることが重要です。まず、Jobの責務を再実行単位に合わせます。次に、retryとtimeoutを処理の性質から決めます。さらに、冪等性、failed jobs、監視、手動復旧まで設計に含めます。
LaravelのQueueは、メール送信や通知だけでなく、業務系システムの外部連携、バッチ処理、運用改善でもよく使われます。そのため、非同期処理や運用まで見据えた設計経験は、案件選びでも強みになります。
Laravelや非同期処理、保守改善の経験を次の案件でどう活かすか迷っている方は、技術領域や担当範囲を整理するところから相談できます。
一度カジュアル面談をしませんか?
株式会社bluenaは「高還元」と「伴走支援」を両立したSES企業です。単価の81〜86%を還元する報酬体系と、専任サポーターによる隔週1on1で、エンジニアが納得できるキャリアを実現します。
まとまっていなくてもOK——まずは現在地を聞かせてください。
カジュアル面談ですので、お気軽にお聞かせください。





