Overview
Laravel’s queue system provides two strategies for controlling job execution: Unique Jobs and Debounced Jobs. Both prevent unnecessary work when the same job is dispatched multiple times, but they work differently.Unique Jobs — ShouldBeUnique
Prevents duplicate dispatches while an instance of the job is already on the queue.
UpdateSearchIndex is on the queue (or being processed), any new dispatches of the same job will be silently ignored.
Scoping Uniqueness with a Key — UniqueFor + uniqueId()
When the same job class handles different entities (e.g., product A vs. product B), define a uniqueId() method to scope the uniqueness constraint.
- The value returned by
uniqueId()becomes the cache lock key. #[UniqueFor(seconds)]sets a timeout after which the lock is automatically released — a failsafe for stuck jobs.
Customizing the Cache Driver — uniqueVia()
To use a specific cache driver instead of the default:
Unique Jobs require a cache driver that supports atomic locks:
redis, database, memcached, dynamodb, file, or array.ShouldBeUnique vs ShouldBeUniqueUntilProcessing
With ShouldBeUnique, the lock is held until the job completes or exhausts its retry attempts. This can be a problem in some scenarios.
Example: UpdateSearchIndex(product_id: 42) is on the queue. A worker picks it up and starts processing. You want to dispatch the same job again — but the lock is still held, so the new dispatch is ignored until processing finishes.
ShouldBeUniqueUntilProcessing releases the lock immediately before processing begins, allowing a new dispatch the moment a worker picks up the job.
Comparison
Debounced Jobs — #[DebounceFor]
The
DebounceFor attribute was added in Laravel 13.debounceId()identifies which dispatches are considered “the same job” (eachproductIdis debounced independently).- If
UpdateSearchIndexfor product 42 is dispatched 10 times within 30 seconds, only the last dispatch will run.
maxWait — Capping the Deferral Window
For frequently updated data, debouncing could postpone execution indefinitely. Set maxWait to cap how long a job can be deferred.
Customizing the Cache Driver — debounceVia()
JobDebounced Event
When a job is superseded by a newer dispatch, Laravel fires Illuminate\Queue\Events\JobDebounced and removes the superseded job from the queue. Listen to this event for monitoring or logging.
Choosing the Right Approach
Internal Implementation
Lock Key Format for Unique Jobs
When aShouldBeUnique job is dispatched, Laravel acquires a cache atomic lock using the following key format:
How Debounced Jobs Work
DebounceFor uses a cache entry to manage a “debounce window”. On each new dispatch:
- The existing queued job is removed (
JobDebouncedevent fired) - The new job is added to the queue with a delay equal to the debounce seconds
- The debounce timer resets
maxWait is specified, the timestamp of the first dispatch is also recorded, capping the total deferral time.