Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Schedule Ketchup Laravel Package

clingen-data-model/laravel-schedule-ketchup

Laravel Schedule Ketchup adds catch-up support to Laravel's task scheduler, helping you detect and run scheduled jobs missed during downtime. Simple configuration, familiar scheduler integration, and reliable recovery for time-sensitive tasks.

View on GitHub
Deep Wiki
Context7

Getting Started

This is the first tagged release (v1.0.0) of clingen-data-model/laravel-schedule-ketchup. The published changelog contains exactly one substantive item — a GitHub Actions workflow to run tests — so the tag is best read as "the code that already existed is now versioned and CI-verified" rather than a feature announcement. There is no prior API surface to compare against, so treat everything below as the starting contract and verify names against the README and src/ before you build on them.

Minimal path in:

  1. composer require clingen-data-model/laravel-schedule-ketchup and pin it ("^1.0") — you are adopting a 1.0.0 with a single published release.
  2. Open the GitHub Actions workflow file first. It is the only thing v1.0.0 actually adds, and it doubles as the package's support matrix: the PHP and Laravel versions in that matrix are the versions the author is actually testing. Match your app to it before anything else.
  3. Publish and read the config if a service provider offers it (php artisan vendor:publish --provider="...ScheduleKetchupServiceProvider"), then read the service provider's register()/boot() to see what is bound into the container and whether the scheduler is touched at boot.
  4. Run php artisan schedule:list before and after install to confirm nothing is silently re-registered.

First use case: a scheduled task that must run on a fixed cadence — a nightly digest, a cache warm, a sync job — where the host was down, deploys overlapped, or a worker was stalled during the window. The "ketchup" pattern is to detect the missed window and run the task on the next opportunity instead of waiting a full interval. Start by wrapping one low-risk, idempotent command and confirm the behaviour end-to-end before widening it.

Implementation Patterns

Wrap, don't rewrite. Keep your existing $schedule->command(...) definitions and add the catch-up behaviour around them. The migration cost of this package should be near zero; if you find yourself restructuring your schedule file, stop and re-read the README.

Register where your schedule already lives. In Laravel 11+ that is typically routes/console.php (or bootstrap/app.php via ->withSchedule()); on Laravel 10 and below it is app/Console/Kernel.php::schedule(). Keep the catch-up entries colocated with the normal entries so a reviewer can see both in one diff.

Only put idempotent work behind it. Catch-up semantics mean a task can execute outside its nominal window, and potentially more than once across a restart. Good candidates: report generation, search index refresh, cache priming, digest emails, reconciliation pulls from a remote API. Bad candidates: payment capture, one-shot notifications, anything with side effects that assume "exactly once per night."

Compose with the scheduler guards you already trust. Chain ->withoutOverlapping() and ->onOneServer() on any catch-up entry that runs on more than one node. withoutOverlapping() is backed by the cache lock, so confirm all nodes share one cache store — a file or array cache on a multi-node fleet gives you the lock in name only.

Cap the stampede. The failure mode of a catch-up scheduler is the opposite of the problem it solves: after a long outage, every missed window becomes due at once. If the package exposes a max-catch-up or lookback setting, set it explicitly rather than accepting the default (see Gotchas). If it does not, wrap your command body with your own "skip if we are more than N intervals behind" check.

Make catch-up runs observable. Emit a distinct log line or metric when a task runs because a window was missed, not because it was on time. Without that signal you cannot tell a healthy scheduler from one that is silently backfilling every night, and you will not notice a task that has effectively stopped running on schedule.

Test with a frozen clock. Carbon::setTestNow() to a timestamp past the expected window, then assert the task fires — and to a timestamp inside the window, then assert it does not. This is the only reliable way to exercise catch-up logic in a test suite, and it is what the newly added CI workflow is presumably doing.

Gotchas and Tips

  • v1.0.0 has no behavioural changelog. The only listed change is CI. Before adopting, read the source and the README rather than inferring features from the version number; before upgrading past it, diff the tags because the release notes may not describe the actual delta.
  • A CI-only release tells you something useful, and something cautionary. Useful: the maintainer now runs the test suite on every push, so regressions are more likely to be caught. Cautionary: it is a single-contributor (@bpow) initial tag with one PR merged — budget for the API to move and pin your constraint.
  • Timezone and DST. The scheduler evaluates cron expressions in the app timezone (config/app.php). A DST transition creates one phantom miss and one phantom double-window per year. If the package stores last-run timestamps, check whether it stores them in UTC or app-local before you trust the arithmetic across a November rollback.
  • Downtime length vs interval width. A 5-minute interval task after a 6-hour outage has 72 missed windows. Decide deliberately whether you want all 72, one, or one-per-new-window, and configure accordingly — the default may not be what you assume.
  • Queue interaction. Most scheduled tasks dispatch to a queue. If your queue workers were also down during the outage, catch-up merely re-queues work that then sits behind the backlog. Check queue depth after any restart, not just the scheduler.
  • Cache lock leakage. If a catch-up run is killed mid-flight (deploy, OOM), a stale withoutOverlapping() lock can suppress the next several runs. Know how to clear it (php artisan cache:clear on the relevant store, or the package's own reset if it provides one).
  • Debugging loop: php artisan schedule:list to confirm registration → php artisan schedule:run in a console to see it execute synchronously → check storage/logs/laravel.log filtered to the package namespace → then simulate a miss with Carbon::setTestNow() in php artisan tinker. Work that order; it isolates registration, execution, and trigger logic in three cheap steps.
  • Extension points to look for: a publishable config (lookback window, catch-up cap, store selection), the container binding used to track last-run state, and whether the service provider exposes an event on catch-up. The state store is the most valuable one to swap — pointing it at your shared cache or database is usually what makes the package safe on a multi-node deployment.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource