How to Update Large Eloquent Tables Without Skipping Rows with lazyById
When a Laravel job updates many database rows, an update can make a row stop meeting the query’s conditions. If the job…
Laravel’s scheduler starts commands at configured times. If a command takes longer than the time between scheduled runs, the next run can start before the previous one finishes. That overlap can duplicate work or send repeated requests.
Use withoutOverlapping() on the scheduled event so Laravel places a lock in its cache before running the command. While the lock exists, Laravel skips that event. The lock is removed after the command finishes, subject to its expiration time.
withoutOverlapping() to the scheduled eventAdd withoutOverlapping() to the event that schedules the command. For example, this command runs every minute, but Laravel skips a run when the previous one still holds the lock:
use Illuminate\Support\Facades\Schedule;
Schedule::command('reports:sync')
->name('reports-sync')
->everyMinute()
->withoutOverlapping();
Laravel’s default expiration is 24 hours when you omit the argument. The 24-hour timeout prevents a crashed command from blocking future runs indefinitely. If a command exits without clearing its lock, Laravel can still skip it for a long time. The behavior and expiration setting are described in this overview of Laravel’s scheduler mutex.
Give the event a name before calling withoutOverlapping() when it needs an explicit identity. A reported scheduler error shows that Laravel can require a scheduled event name to create the overlap lock.
Laravel stores the lock in the configured cache. The available cache driver determines where Laravel keeps the lock. It may keep the lock in files or Redis. A database and Memcached are other options. If multiple servers run the scheduler, they need to use a cache they share; separate local file caches do not give those servers a shared lock.
The lock also relies on a working cache configuration after you change Laravel or its cache driver. A Laravel issue report describes a withoutOverlapping() problem with a particular Laravel version and DynamoDB cache configuration. Treat that report as a reason to test scheduled tasks after upgrades, not as evidence that the method fails with every DynamoDB setup.
If a command crashes before its lock is removed, Laravel skips the event until the lock expires or someone clears the scheduler cache. Run:
php artisan schedule:clear-cache
Avoid using php artisan cache:clear as the first response. That command clears the application cache broadly, while schedule:clear-cache targets scheduler locks. A reported troubleshooting case describes a stale lock preventing a scheduled task from running.
Add logging around long-running commands so you can tell whether a task is still running, has failed, or stopped before completing. When the lock expires but the command remains active, Laravel has no way to know that the earlier process is still running, so the next scheduled run can overlap it.
withoutOverlapping() protects a particular scheduled event. If the same command must also avoid concurrent runs when someone starts it manually or another code path invokes it, Laravel’s Isolatable command feature addresses that broader case. An isolatable command supports the --isolated option, which uses a lock to prevent another instance from running at the same time. See the discussion of scheduler mutexes and command isolation.
Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.
Take a look at vroni.com