spatie/laravel-short-schedule
Run Laravel Artisan commands at sub-minute intervals (every second or even 0.5s). Adds a short-scheduler powered by a ReactPHP event loop, running separately from schedule:run so high-frequency tasks don’t block or get delayed.
## Getting Started
### Minimal Setup
1. **Installation**: Add the package via Composer:
```bash
composer require spatie/laravel-short-schedule
php artisan vendor:publish --provider="Spatie\ShortSchedule\ShortScheduleServiceProvider"
shortSchedule method to your app/Console/Kernel.php:
protected function shortSchedule(ShortSchedule $shortSchedule)
{
$shortSchedule->command('your-command')->everySecond();
}
php artisan short-schedule:run
Use a process manager like Supervisor to keep it running persistently.Schedule a command to run every 5 seconds:
ShortSchedule::command('your-command')->everySeconds(5);
shortSchedule method in Kernel.php or the ShortSchedule facade in console.php to define tasks.
// Kernel.php
protected function shortSchedule(ShortSchedule $shortSchedule)
{
$shortSchedule->command('cache:clear')->everyMinutes(1);
}
Sub-Second Precision:
ShortSchedule::command('log:clean')->everySeconds(0.5); // Runs every half-second
Shell Commands:
ShortSchedule::exec('php artisan optimize')->everyHour();
Constraints:
ShortSchedule::command('sync-data')->between('09:00', '17:00')->everySecond();
ShortSchedule::command('deploy')->environments(['staging', 'production'])->everyMinute();
ShortSchedule::command('notify-users')->when(fn() => User::count() > 100)->everyMinute();
ShortSchedule::command('backup')->everyHour()->withoutOverlapping();
ShortSchedule::command('critical-task')->everySecond()->runInMaintenanceMode();
ShortSchedule::command('leader-election')->everyMinute()->onOneServer();
Event Listeners: React to task execution (use queues for heavy logic):
ShortScheduledTaskStarting::dispatch($command, $process);
ShortSchedule::fake() (if available) or test ReactPHP loops directly (see Spatie’s testing video).Blocking the Loop:
when, between) and event listeners run inside the ReactPHP loop. Heavy logic here will delay all tasks.when closures).// Bad: Heavy logic in constraint
ShortSchedule::command('slow-task')->when(fn() => heavyDatabaseQuery())->everySecond();
// Good: Pre-compute or cache
ShortSchedule::command('slow-task')->when(fn() => Cache::remember('task_condition', 60, fn() => heavyDatabaseQuery()))->everySecond();
Memory Leaks:
--lifetime flag to restart the worker periodically:
php artisan short-schedule:run --lifetime=3600 # Restart every hour
Supervisor Misconfiguration:
[program:short-schedule]
command=php /path/to/artisan short-schedule:run --lifetime=3600
autostart=true
autorestart=true
user=www-data
numprocs=1
stderr_logfile=/var/log/short-schedule.err.log
stdout_logfile=/var/log/short-schedule.out.log
Command Resolution:
ShortSchedule::command(MyCommand::class)), ensure the class is autoloaded and the handle() method exists.config/app.php under providers.Timezone Sensitivity:
between method uses the server’s timezone. Ensure it matches your expectations..env:
APP_TIMEZONE=UTC
Overlapping Tasks:
withoutOverlapping() to prevent this, but beware of race conditions in distributed environments.onOneServer() for single-server deployments.Logs:
/var/log/short-schedule.out.log (or your Supervisor log path) for errors.php artisan short-schedule:run --verbose
Process Inspection:
ps aux | grep short-schedule
pkill -f short-schedule
Testing:
ShortSchedule::fake() (if available) to mock the scheduler in tests.use Spatie\ShortSchedule\Tests\TestCase;
public function test_constraint()
{
$this->assertTrue($this->app->make(ShortSchedule::class)->constraint()->evaluate());
}
Environment Issues:
production). Use the environments() constraint to restrict execution:
ShortSchedule::command('task')->environments('production')->everySecond();
Custom Constraints:
Extend the Spatie\ShortSchedule\Constraints\Constraint class to create reusable constraints:
namespace App\ShortSchedule\Constraints;
use Spatie\ShortSchedule\Constraints\Constraint;
class CustomConstraint extends Constraint
{
public function evaluate(): bool
{
return customLogic();
}
}
Use it in schedules:
ShortSchedule::command('task')->constraint(new CustomConstraint())->everySecond();
Event Extensions:
Listen to events like ShortScheduledTaskStarting to add pre/post-task logic:
ShortScheduledTaskStarting::listen(function ($command, $process) {
Log::info("Starting {$command} at " . now());
});
Process Lifecycle:
Override the Spatie\ShortSchedule\ShortSchedule class to customize process creation (e.g., environment variables):
$shortSchedule->command('task')->everySecond()->withOptions(['--env' => 'production']);
ReactPHP Customization:
For advanced use cases, extend the Spatie\ShortSchedule\ShortSchedule class to modify the ReactPHP loop behavior (e.g., custom timers or signals).
everySeconds(1) with withoutOverlapping() to create a simple rate limiter for external APIs.$shortSchedule->clear();
$shortSchedule->command('dynamic-task')->everySeconds(config('
How can I help you explore Laravel packages today?