目次
Laravelで注文を保存し、その直後のメールや集計をキューへ送ったところ、ワーカー側だけが「注文が見つからない」と失敗する。Web画面では保存に成功しているのに、後続処理を再実行すると直る。この症状では、遅延や再試行の回数を増やす前に、DBのとジョブ投入の順序を確認します。
本稿は2026年10月31日に確認したLaravel 13.x公式キュードキュメントに基づく実践解説です。新機能の発表ではありません。対象はDB更新と非同期ジョブを組み合わせるアプリで、例は単一DB接続と別プロセスのワーカーを想定します。掲載コードは未実行の説明例であり、実環境での保証ではありません。
スポンサーリンク
保存処理の呼び出しと確定は同じ時点ではない
トランザクション内でモデルを保存しても、別接続から確定済みのデータとして読める時点は別です。先にRedisなどへジョブを投入し、ワーカーがすぐ取得すれば、親のコミットを待たず処理を始めることがあります。Laravelの資料も、この場合は更新内容がまだ反映されていなかったり、作成したレコードが存在しなかったりすると説明しています。
発生頻度が低いことは、順序が正しい証拠にはなりません。開発端末では保存の後に手動でワーカーを起動していた、検証環境では同じプロセスで処理していた、といった差を調べます。本番の負荷が低いときだけ顕在化する競合も考えられるため、単純に「高負荷だから遅れた」と決めず、各処理の時刻と接続を追います。
まず一つのジョブにafterCommitを付ける
個別の投入時点はPendingDispatchの公式APIでも確認できます。afterCommitはDBトランザクションの確定後に投入する指定です。以下は、注文の作成と、そのIDを使う集計ジョブを組み合わせた例です。実際の注文に必要な入力検証や項目は省略しています。
use App\Jobs\BuildOrderSummary;
use App\Models\Order;
use Illuminate\Support\Facades\DB;
$orderId = DB::transaction(function () {
$order = new Order();
$order->status = 'pending';
$order->save();
BuildOrderSummary::dispatch($order->getKey())
->afterCommit();
return $order->getKey();
});
BuildOrderSummaryは、IDを受け取るコンストラクターとキュー投入に必要な構成を持ち、ShouldQueueを実装する非同期ジョブとして用意する前提です。Orderモデルとテーブルもアプリ側で定義します。この断片だけを貼り付けて注文機能が完成するわけではありません。まず既存ジョブの一経路で、投入を待つ位置を明示する例として使います。
DB::transactionの挙動は公式DBドキュメントに従います。クロージャー内の例外時はされ、正常終了時はコミットされます。例外を握りつぶして正常終了させると、意図した失敗扱いになりません。どのエラーを外へ返すかも、ジョブを待つ指定とは別に確認してください。
afterCommitを付けても、ジョブがその場で完了するわけではありません。DB確定後にキューへ渡すタイミングを制御する指定です。画面で「集計済み」と表示するなら、投入できたことと集計が終わったことを分け、完了結果を別に確認します。応答の成功を、後続の全業務が終わった証拠にしない設計が必要です。
スポンサーリンク
接続全体へ適用する設定は影響範囲を調べる
複数のジョブが同じ規則を必要とする場合は、config/queue.phpの対象接続へafter_commitを設定できます。次はRedis接続の既存設定に追加する一項目を示す断片です。接続先やキュー名など他の設定を削除して、この断片だけへ置き換えないでください。
// config/queue.php の対象接続にある設定項目
'after_commit' => true,
個別のafterCommitと接続設定は、適用範囲が異なります。接続全体の変更は、関連するキュー済みリスナー、メール、通知、ブロードキャストイベントにも影響します。注文ジョブ一つを直すつもりで、別の画面の通知時点まで変わらないかを棚卸しします。呼び出し箇所と実際に使う接続を対応付け、既定接続だけを見て終えないことが大切です。
公式資料では、確定待ちにしたジョブはトランザクションがロールバックされた場合に破棄され、開いたトランザクションがなければ直ちに投入されます。したがって、どこで呼んでも一定秒数遅らせる設定ではありません。ネストした処理では内側の関数が戻った時点を確定とみなさず、親のトランザクションの境界まで確認します。
数秒のdelayを確定待ちの代わりにしない
遅延を付けると症状が減ることはあっても、DBがその秒数内に必ずコミットするという条件は作れません。ロック待ちや別処理の遅延が加われば、設定時間を超える可能性があります。逆にすぐ確定した要求にも待ち時間を加えます。時間による待機が業務上必要な場合と、確定順序を守りたい場合を分けて指定します。
ジョブへモデルを渡す場合も、呼び出し元のメモリー上の値が丸ごと固定されると仮定しないでください。Laravelのモデルを扱うキューの仕組みでは、実行時にDBから取得し直す経路があります。IDを明示して渡す例でも、処理時にどの状態を読めばよいかを決めます。投入時の状態そのものが必要なら、必要な値とその版を別途設計します。
スポンサーリンク
確定後に投入しても一度きりの実行にはならない
afterCommitが扱うのは順序です。DBの確定、キューへの配送、ワーカーでの処理、外部サービスへの送信を、全体で一つの原子的な操作に変える機能ではありません。これは処理の境界を分けて考えた本サイトの設計上の注意点です。キューへの投入が失敗した場合や、ワーカーが途中停止した場合の照合方法は別に必要です。
例えば外部へ送信した直後にジョブが停止すれば、再実行時に同じ送信を重ねる可能性があります。DBに「完了」と書くだけでも、外部の成功と記録の間に空白が残ります。外部サービスが提供する冪等性の仕組み、処理済みの識別、結果不明時の問い合わせなど、実際の契約に沿って設計してください。今回の例のジョブ名だけから、その対策があるとは扱いません。
重要な後続処理を失いたくない場合は、注文と同じDBトランザクションに送信予定の記録を保存し、別の処理が配送するアウトボックスも検討できます。ただし方式名だけで解決したとはみなさず、未配送の検出、再送、重複の扱い、保存期間を定めます。単純な集計を一つ遅らせる変更と、配送を保証する設計は、必要性に応じて分けて評価します。
同期のテストだけで確定待ちを検証しない
では本番と同じ種類の非同期接続と、別プロセスのワーカーを使います。まず、保存を確定する前にワーカーが動ける条件を作り、未対策の経路とafterCommitを付けた経路を比べます。試験用の注文と副作用のないジョブを用意し、実際の顧客への通知や課金を発生させず、IDと観測時刻を記録する構成から始めます。
確認するのは、成功時に確定後のレコードを読めること、意図的なロールバック時に対象ジョブが投入されないこと、トランザクション外の呼び出しが不要に待たされないことです。ネストした処理がある場合は親の確定前後も試します。テストの仕組みがDBをトランザクションで包む場合は、その影響を切り分け、テスト終了時の巻き戻しだけを本番の挙動と混同しません。
Queueをfakeにしたテストは呼び出しの意図を確かめるのに使えますが、別接続のワーカーが何を読んだかまでは証明しません。単体テスト、実接続での統合試験、運用環境の観測を分けて記録します。一回成功したことより、狙った確定境界を通したことと、失敗時にも期待する状態になったことを完了条件にします。
タイムアウトと配備も別の境界として扱う
確定前の参照を直した後も二重実行が残るなら、再試行の条件を調べます。公式キュー資料は、ワーカーのtimeoutをretry_afterより数秒以上短くする必要を説明しています。先のワーカーがまだ処理している間にジョブが再び取得可能になる設定を避けるためです。Amazon SQSではretry_afterではなく可視性タイムアウトを確認するため、接続ごとの仕様で判断します。
長時間動くワーカーは、ソースを書き換えただけで新しいコードを読み直すとは限りません。配備時は、利用するプロセス管理に合った再起動と起動確認を含めます。queue:restartを使う構成なら、再起動シグナルを扱うや、自動的に起動し直す監視の設定も確認します。管理型の基盤では、そのサービスのワーカー更新手順に従います。
記録は失敗件数より処理のつながりを残す
監視には、要求、注文ID、ジョブ、処理の試行を対応付けられる識別情報を残します。注文が確定した時刻、キューへ渡した結果、ワーカーが読んだ対象、処理の完了を区別すると、確定前の参照と配送失敗、処理後の停止を分けやすくなります。個人情報や注文内容全体をログに出す必要はありません。調査に必要な範囲へ絞ります。
最初の変更は、DB保存へ依存するジョブ一つへ限定し、確定後の投入と失敗時の状態を確かめます。対象を広げるときは、接続設定が他の通知へ与える影響を確認してください。afterCommitによる順序の制御と、処理を失わず重ねないための設計を分けて扱えば、応急的な遅延の追加より、障害時に説明できる構成へ近づけられます。
スポンサーリンク
関連記事
在庫やクーポンの同時実行と再試行を分ける設計では、DBの業務条件と外部の副作用を整理しています。本稿はLaravelのジョブ投入時点を扱います。afterCommitの適用後も、在庫更新の競合や外部処理の重複は別に設計してください。
参考資料
Laravel 13.x:Queues(DBトランザクション・再試行・ワーカーの配備)
Laravel 13.x:Database Transactions
Laravel 13.x API:PendingDispatchとafterCommit




