An editor changes a title, fixes the summary, and adjusts a category. Sending three complete search updates during that short burst does unnecessary work. A useful design is to save every edit immediately, then refresh the derived search document once the editor pauses. Laravel 13 now documents a dedicated queue API for that pattern.
The Laravel queue documentation describes DebounceFor, a job attribute that lets a newer dispatch supersede an earlier one with the same debounce identity. The example below applies that API to an article index.
This example assumes your application already has an Article model and a RefreshArticleSearchDocument action. The action should build the search document from the current database record and update the corresponding document in your search provider.
namespace App\Jobs;
use App\Actions\RefreshArticleSearchDocument;
use App\Models\Article;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Queue\Attributes\DebounceFor;
#[DebounceFor(30, maxWait: 120)]
final class RefreshArticleSearch implements ShouldQueue
{
use Queueable;
public function __construct(public readonly int $articleId) {}
public function debounceId(): string
{
return 'article:'.$this->articleId;
}
public function handle(RefreshArticleSearchDocument $refresh): void
{
$refresh->handle(Article::query()->findOrFail($this->articleId));
}
}The attribute sets a 30-second debounce window and caps repeated postponement at 120 seconds. Use a central cache when dispatching from multiple servers, and do not combine this attribute with ShouldBeUnique. Those are requirements of the documented queue mechanism.
For this design, an article ID is enough information to enqueue. Loading the record when the job runs makes the index reflect the saved article at execution time. Passing an earlier title in the constructor would make it easier to accidentally write an obsolete document.
Decide explicitly how your action handles publication changes. If the record is now a draft, its search document should be removed. If articles can be permanently deleted, define a separate removal workflow or adapt the refresh action to accept an ID and handle a missing record. The illustrative job above deliberately fails for a missing article so that this decision cannot pass unnoticed.
Place the dispatch after the successful save and database commit. An indexer should never advertise a title from a transaction that later rolls back. Keep the actual database write responsive; the queue owns only the derived search work.
Search refreshes, preview generation, and recalculated summaries are good candidates when the latest saved state is what matters. A purchase ledger needs every transaction recorded. If discarding an intermediate event changes the business outcome, choose a different job design.
A debounce ID also deserves a domain review. In a multitenant application where identifiers are only unique within a tenant, include that tenant in the identity. Two customers editing article 17 must never suppress each other’s work.
Dispatch repeated edits for one article, then confirm its final search document matches the last saved state.
Edit two articles in the same window and confirm both refresh independently.
Keep dispatching beyond the maximum wait and observe that work still becomes eligible to run.
Exercise draft, deletion, provider failure, and retry behavior using the same queue and cache types as production.
A queue fake can show that application code dispatched a job. It cannot establish that a running worker, shared cache, and search provider produce the intended final result. Record both the input burst and the final indexed document when checking this feature. That evidence tells you whether the optimization preserved correctness.