Here's the problem we are trying to solve:
Each time a product is updated, we want to dispatch a job that updates the search index. If several products were updated during a short period of time, we don't want to dispatch multiple jobs. There should be only a single index update job in the queue at any time.
To solve this problem, we are going to use cache locks to prevent dispatching UpdateProductsIndex
jobs while another instance of the job is in the queue. So in the controller's update
method, we're going to dispatch the job only if a lock was successfully acquired:
public function update($product)
{
$product->update(...);
$lock = Cache::lock('productsIndex', 120);
if ($lock->get()) {
UpdateProductsIndex::dispatch();
}
}
This lock will hold for 120 seconds, you may update that to a value that makes sense in your use case.
Now inside the handle
method of the job, we're going to release the lock after the job finishes:
public function handle()
{
// Update the search index
Cache::lock('productsIndex')->forceRelease();
}
Notice here we use forceRelease()
on the lock because we don't care who the lock owner is. For more information about this, check the official laravel documentation.
We may also release the lock inside the failed
method of the job class:
public function failed(Throwable $exception)
{
Cache::lock('productsIndex')->forceRelease();
}
With these simple changes, we made sure only a single UpdateProductsIndex
job will be in the queue at any point in time.
If you want to learn more about Laravel's queue system, make sure to check Laravel Queues in Action! I've put everything I know about the queue system in an eBook format along with several real-life uses cases. Check it out for a crash course, a cookbook, a guide, and a reference.