概要
Laravel のキュー機能には、ジョブの 重複排除(Unique) と デバウンス(Debounce) という2種類の実行制御が用意されています。どちらも「同じジョブを何度もディスパッチしたとき、無駄な実行を省く」ための仕組みですが、動作が異なります。Unique Jobs — ShouldBeUnique
同一ジョブがキューに存在する間は、追加のディスパッチを無視します。
UpdateSearchIndex がキューに積まれている(または処理中)の間、同じジョブをディスパッチしようとしても無視されます。
キーでユニーク制約を絞り込む — UniqueFor + uniqueId()
同じジョブクラスでも「商品 A の更新」と「商品 B の更新」は別ジョブとして扱いたい場合、uniqueId() メソッドでキーを定義します。
uniqueId()が返す値がキャッシュロックのキーになります。#[UniqueFor(秒数)]を指定すると、その秒数が経過した後にロックが自動解放されます(ジョブが処理されなかった場合のフェイルセーフ)。
キャッシュドライバーを指定する — uniqueVia()
デフォルトのキャッシュドライバー以外を使用したい場合は uniqueVia() を実装します。
Unique Jobs はアトミックロックをサポートするキャッシュドライバー(
redis、database、memcached、dynamodb、file、array)が必要です。ShouldBeUnique vs ShouldBeUniqueUntilProcessing
ShouldBeUnique のロックはジョブが完了またはリトライ上限に達するまで保持されます。これが問題になるケースがあります。
例: キューに UpdateSearchIndex(product_id: 42) が1件あり、ワーカーが処理を開始した直後に同じジョブを再ディスパッチしたい場合。ShouldBeUnique では処理完了まで2件目がキューに入りません。
この場合は ShouldBeUniqueUntilProcessing を使います。ロックが処理開始直前に解放されるため、ワーカーがジョブを取り出した瞬間に次のディスパッチが可能になります。
比較まとめ
Debounced Jobs — #[DebounceFor]
DebounceFor アトリビュートは Laravel 13 で追加された機能です。debounceId()が返す値でジョブを識別します(商品 ID ごとに独立したデバウンスが適用)。- 30秒の間に同じ
productIdで10回ディスパッチされても、最後の1件だけが実行されます。
maxWait — 最大待機時間の上限
頻繁に更新されるデータでは、デバウンスが延々と続いてジョブが永遠に実行されない可能性があります。maxWait で最大遅延時間を設定できます。
キャッシュドライバーを指定する — debounceVia()
JobDebounced イベント
後続のディスパッチによって上書きされたジョブは、Illuminate\Queue\Events\JobDebounced イベントを発行してキューから削除されます。このイベントをリッスンすることで、デバウンスされたジョブの追跡や監視が可能です。
どれを使うべきか
内部実装
Unique Jobs のロック機構
ShouldBeUnique ジョブがディスパッチされると、Laravel は内部でキャッシュのアトミックロックを取得します。ロックキーは以下のフォーマットです:
Debounced Jobs の実装
DebounceFor は内部的に「デバウンスウィンドウ」を管理するキャッシュエントリを使います。新しいディスパッチが来るたびに:
- 既存のジョブをキューから削除(
JobDebouncedイベント発行) - 新しいジョブをキューに追加(デバウンス秒数の遅延付き)
- キャッシュのタイマーをリセット
maxWait が指定されている場合、初回ディスパッチのタイムスタンプも記録し、その時刻から maxWait 秒を超えるデバウンスを防ぎます。