Repository navigation
Replies: 3 comments 5 replies
|
The most useful part of this proposal to me is having queryable scheduling state outside the queue. Before choosing between inheritance and a fluent API, I'd make the identity and completion point of an occurrence explicit. In your queued example,
Either can stay linear. Leaving the distinction implicit makes “retry the same occurrence” ambiguous when A stable occurrence identity would help both designs: for example For a database-backed sequence handing work to an external queue, I'd validate the reference package with three fault-injection cases: two runners claiming the same occurrence, a crash between durable state change and queue publication, and a stale queued occurrence after cancellation/rescheduling. For time semantics, specify whether downtime causes catch-up, skipping, or coalescing, and whether recurring dates are anchored to scheduled time or actual completion time. Those contracts would give the package a concrete basis for deciding what belongs in the framework. This is design feedback on the proposal, not a claim that the reference implementation currently fails those cases. |
|
The naming could be confusing. I would probably rename |
Update — v0.1.0 releasedThe reference implementation is now available on Packagist: composer require ai-soft/laravel-scheduled-sequenceVersion The reliability test task discussed above is now closed after the GitHub Actions matrix passed. The package remains an early Package: https://packagist.org/packages/ai-soft/laravel-scheduled-sequence |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Summary
This proposal explores a first-class Laravel abstraction for persistent sequences of actions that occur at irregular times relative to an entity-specific start time.
A Scheduled Sequence persists its current scheduling state, evaluates application state immediately before each occurrence, and may complete, cancel, or continue recurring after its configured sequence.
It is intended to complement Laravel's existing Scheduler and queues rather than replace them.
The important distinction is not simply how an action is executed later, but where the authoritative scheduling state lives.
A working package implements the core concept. Its design evolved from a predecessor used in production for two years.
Motivation
Some application schedules do not fit a recurring cron expression.
Consider an account that becomes unpaid. The business requirement may be:
Every account starts this sequence independently.
Before each occurrence, the application must also check whether the account is still unpaid. If it has been paid, the remaining sequence should be cancelled.
The schedule maps naturally to a declaration such as:
This is not naturally represented by a cron expression.
It can be implemented using delayed jobs, but the application must then manage additional concerns such as:
The underlying abstraction is relatively small:
Proposed API
The following API is illustrative and based on the reference implementation.
It is included to make the proposed abstraction concrete, not as a proposed final framework API.
A sequence could be represented by a dedicated class:
The sequence could then be started for an application entity:
An
Occurrenceobject could expose contextual information such as:The exact namespace, inheritance model, offset representation, and method names are intentionally open for discussion.
This Is Not Notification-Specific
Notifications are an easy real-world example, but Scheduled Sequences describe temporal application behavior generally.
For example, a provisioning process might require:
It could be represented by:
The abstraction is therefore about irregular temporal sequences, not notification delivery.
Persistent Scheduling State
The main difference from delayed jobs is where the authoritative future scheduling state is stored.
With multiple delayed jobs, future scheduling state effectively exists inside the queue:
With a Scheduled Sequence:
The queue may still execute the action, but it is not the canonical representation of the future schedule.
A Scheduled Sequence would persist only the state required to determine its next occurrence.
Conceptually:
An Eloquent polymorphic relationship is one natural implementation, and is what the reference package currently uses.
However, the abstraction itself does not necessarily need to be restricted to Eloquent models.
The full set of future timestamps does not need to be stored. Future occurrences are derived from the sequence definition and its
start_at.Conditional Continuation
An important characteristic is that current application state is evaluated immediately before an occurrence executes.
For example:
If the account becomes paid between the 7-day and 10-day occurrences, the next execution detects that state and cancels the remaining sequence.
This keeps the temporal definition and the business condition controlling its lifetime together.
Recurrence After an Irregular Prefix
A sequence may optionally transition from irregular offsets into regular recurrence.
For example:
represents:
No unbounded collection of future jobs needs to be created.
The sequence only needs to know its current state and next occurrence.
Relationship to Laravel Scheduler
Scheduled Sequences would not replace Laravel's Scheduler.
An application could continue using a normal Laravel schedule:
The runner only needs to find active sequences where:
and process those occurrences.
The distinction is:
versus:
The current reference package registers the minute runner automatically, so applications do not need to add this command to their scheduler manually.
The framework-level responsibility remains the same: Laravel's Scheduler wakes the package runner.
Relationship to Queues
Scheduled Sequences would also not replace queues.
An occurrence may execute synchronously or dispatch normal queued work:
Queues remain the execution mechanism.
The sequence owns the persistent scheduling state.
Why Not Use Delayed Jobs?
Laravel already supports delayed jobs:
For a single future action, this is usually exactly the right abstraction.
Even a finite sequence can be represented by dispatching several delayed jobs:
Scheduled Sequences target a different case: long-lived scheduling state that belongs to an application entity.
When that state is represented entirely by delayed jobs, the application must decide how to:
Another approach is for each job to schedule its successor.
That avoids creating all future jobs in advance, but the job chain then becomes the implicit representation of the schedule.
Scheduled Sequences make this state explicit, persistent, and queryable.
The distinction is therefore not:
It clearly can.
The question is:
Related Laravel Discussions
I could not find an existing Laravel Framework discussion proposing the same combination of:
There are, however, discussions demonstrating parts of the problem.
In Laravel Framework discussion #44197, a developer wanted to schedule a one-time job for a future time derived from a database value and later change that execution time when the database value changed.
The suggested solution was a delayed job.
The developer's next problem was then how to update the delay of the already stored job when application state changed.
That is a simple example of future scheduling state that belongs to application data rather than a static application-level schedule.
Scheduled Sequences address a broader version of that problem by making the scheduling state explicit rather than treating queued jobs as its canonical representation.
Other approaches and packages solve related problems such as dynamically defining application-level schedules or delaying individual actions. Those remain useful, but operate at a different level from a persistent sequence belonging to one application entity.
Lifecycle
The lifecycle is intentionally small:
A Scheduled Sequence therefore has only a few core concepts:
This Is Not a Workflow Engine
Scheduled Sequences deliberately cover a much smaller problem than a general workflow engine.
A Scheduled Sequence has no:
Its topology is always linear:
The timing between those occurrences may be irregular, and execution may stop based on current application state, but there is always only one next occurrence.
A general workflow engine might instead model:
That is explicitly outside the scope of this proposal.
Failure Semantics
Failure behavior should be explicit rather than emerging accidentally from when
next_athappens to be updated.At minimum, a Scheduled Sequence should define whether a failed occurrence should:
A reference implementation may provide attempts and backoff.
External side effects should still use appropriate idempotency mechanisms where available.
Exactly-once execution is not a goal of this proposal.
Detailed retry ledgers, execution history, retention policies, and other operational concerns can be explored by the reference implementation without requiring them to be part of the initial abstraction.
Non-Goals
Scheduled Sequences are not intended to:
The proposed abstraction is intentionally narrow:
Reference Implementation
A working package currently implements this concept with:
next_at-based execution;The reference implementation currently supports Laravel 10–13 and PHP 8.1 or newer.
The package API is still being refined and is not intended to dictate the final Laravel API.
The original production implementation used notification-specific concepts such as:
checkBeforeSend() onExpiredOffset()The framework-facing abstraction proposed here instead uses domain-neutral concepts such as:
shouldContinue() handle() OccurrenceThe reference package exists primarily to validate the abstraction against real-world application workloads.
Whether this ultimately belongs in Laravel itself or remains a community package is intentionally an open question.
The goal of this proposal is first to determine whether the abstraction itself is useful and sufficiently general, rather than to propose that the current package implementation should be merged into Laravel.
Open Questions
ScheduledSequencean appropriate name for this concept?The main question is:
The reference package is intended to help answer that question with real-world usage before any framework integration is considered.
Reference implementation
Version
v0.1.0is available on Packagist:The package supports Laravel 10–13 and PHP 8.1 or newer. It includes durable occurrence identity, recoverable queue publication, stale-work protection, concurrency controls, explicit catch-up policies, retained execution state, and automatic minute-runner registration.
The design evolved from a predecessor used in production for two years. The implementation is an early
0.xrelease, and its public API may evolve before1.0while the abstraction continues to be validated against real application workloads.All reactions