byrcsc/laravel-data-sync · 1.x
Scheduling.
Register schedules from a definition or from config, disable them per definition or entirely, and know what the package schedules on your behalf.
Nothing is scheduled until you ask for it — with one exception: sync:prune,
which runs daily whenever schedule registration is on.
Scheduling from the definition
Give the definition a schedule() method and it is registered automatically:
use Illuminate\Console\Scheduling\Event;
public function schedule(Event $event): void
{
$event->hourlyAt(15)->withoutOverlapping();
}The $event is the scheduled sync:run <name> command, so the whole scheduler
API is available: ->timezone(), ->between(), ->onOneServer(),
->environments(), and so on.
Declaring the method is the opt-in. A definition that does not declare it is not scheduled, and there is no config key that would schedule it implicitly.
Scheduling from config
A cron expression in overrides schedules a definition without touching the
class, and wins over a schedule() method on it:
// config/data-sync.php
'overrides' => [
'products' => [
'schedule' => '0 * * * *',
],
],The expression is validated at boot; an invalid one throws an
InvalidConfigurationException. This is the escape hatch for changing timing per
environment without a deploy of the definition itself.
Turning schedules off
| Goal | Setting |
|---|---|
| Stop scheduling one definition | overrides.<name>.schedule => false |
| Take a definition out of runs and schedules | overrides.<name>.enabled => false |
| Register nothing at all | data-sync.register_schedules => false |
'overrides' => [
'products' => ['schedule' => false],
'legacy-pricing' => ['enabled' => false],
],register_schedules => false also stops the daily sync:prune. If you turn it
off, wire both up yourself:
// routes/console.php
Schedule::command('sync:run products')->hourlyAt(15);
Schedule::command('sync:prune')->dailyAt('02:30');That is the right choice when your scheduling lives entirely in one file, or when an external scheduler — Kubernetes CronJobs, a systemd timer — invokes the commands instead.
What gets registered
With register_schedules on, the package registers, at boot:
sync:prune, daily.sync:run <name>for every enabled definition that has either a cron override or a declaredschedule()method.
Registration order follows data-sync.syncs.
Overlap and drift
Scheduled runs overlap if a feed takes longer than its interval. Two protections apply, and they are not the same thing:
- The discovery lock always applies. A second
sync:runfor the same definition, scheduled or manual, reports that discovery is already running and exits successfully. Processing that is already in flight continues. withoutOverlapping()is yours to add inschedule(). It prevents the command from starting at all while a previous invocation is still running, which is stricter and worth having on the queued path where the command exits long before the work finishes.
For a feed that arrives once daily but at an unpredictable hour, schedule hourly and let the ledger do the deduplication. Re-running with nothing new to process costs one directory listing.
Verifying
php artisan schedule:list
php artisan sync:doctorschedule:list shows the registered commands and their expressions.
sync:doctor verifies the configuration those schedules depend on — sources
reachable, disks writable, tables present — which is what turns a silent 2 a.m.
failure into a deploy-time one.
What to read next
- Running a sync for the command being scheduled.
- Retention and pruning for what the daily
sync:pruneremoves. - Configuration for the full
overridesshape.