Currently Available: Need a skilled Software Developer for your next project?
Categories
Laravel

How to Prevent Overlapping Scheduled Tasks in Laravel

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.

Add withoutOverlapping() to the scheduled event

Add 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.

Choose a cache that scheduler hosts share

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.

Clear a stale lock carefully

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.

Use command isolation for non-scheduled invocations

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.

What I'm building

Delegate tasks. Get software.

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

Email updates

Usually a new article and a few links I found interesting.

No spam. Unsubscribe with one click.

Leave a Reply

Your email address will not be published. Required fields are marked *