Skip to content

Commit 8352787

Browse files
durable-workflow.github.io: update v2 changes
1 parent 98e9165 commit 8352787

File tree

1 file changed

+183
-0
lines changed

1 file changed

+183
-0
lines changed

docs/features/schedules.md

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
---
2+
sidebar_position: 18
3+
---
4+
5+
# Schedules
6+
7+
Schedules let you start workflow runs on a recurring basis using cron expressions. Each schedule is a named, durable entity that the engine evaluates on every tick to determine whether a new run should be triggered.
8+
9+
## Creating a schedule
10+
11+
Use `ScheduleManager::create()` to define a named schedule:
12+
13+
```php
14+
use Workflow\V2\Enums\ScheduleOverlapPolicy;
15+
use Workflow\V2\Support\ScheduleManager;
16+
17+
$schedule = ScheduleManager::create(
18+
scheduleId: 'daily-invoice-sync',
19+
workflowClass: InvoiceSyncWorkflow::class,
20+
cronExpression: '0 2 * * *',
21+
arguments: ['nightly'],
22+
timezone: 'America/New_York',
23+
overlapPolicy: ScheduleOverlapPolicy::Skip,
24+
labels: ['team' => 'billing'],
25+
memo: ['origin' => 'scheduled'],
26+
searchAttributes: ['tenant_id' => '42'],
27+
notes: 'Runs every night at 2 AM ET.',
28+
);
29+
```
30+
31+
The `scheduleId` is a unique, user-chosen identifier for the schedule. Each triggered run gets a deterministic workflow instance ID derived from the schedule ID and trigger timestamp.
32+
33+
### Parameters
34+
35+
| Parameter | Type | Default | Description |
36+
|---|---|---|---|
37+
| `scheduleId` | `string` | required | Unique identifier for the schedule |
38+
| `workflowClass` | `string` | required | The workflow class to start |
39+
| `cronExpression` | `string` | required | Standard cron expression (5 fields) |
40+
| `arguments` | `array` | `[]` | Arguments passed to the workflow's `handle()` method |
41+
| `timezone` | `string` | `'UTC'` | Timezone for evaluating the cron expression |
42+
| `overlapPolicy` | `ScheduleOverlapPolicy` | `Skip` | What to do when the previous run is still active |
43+
| `labels` | `array` | `[]` | Visibility labels applied to each triggered run |
44+
| `memo` | `array` | `[]` | Memo fields applied to each triggered run |
45+
| `searchAttributes` | `array` | `[]` | Search attributes applied to each triggered run |
46+
| `jitterSeconds` | `int` | `0` | Reserved for future random jitter support |
47+
| `maxRuns` | `int\|null` | `null` | Maximum number of runs before auto-deleting the schedule |
48+
| `connection` | `string\|null` | `null` | Queue connection for triggered runs |
49+
| `queue` | `string\|null` | `null` | Queue name for triggered runs |
50+
| `notes` | `string\|null` | `null` | Free-form operator notes |
51+
52+
## Overlap policies
53+
54+
When a schedule fires and the previous run is still active, the overlap policy controls behavior:
55+
56+
| Policy | Behavior |
57+
|---|---|
58+
| `Skip` | Do not start a new run (default) |
59+
| `BufferOne` | Buffer one pending trigger; skip further triggers until the active run completes |
60+
| `AllowAll` | Start the new run regardless of the previous run's state |
61+
| `CancelOther` | Cancel the previous run, then start the new run |
62+
| `TerminateOther` | Terminate the previous run, then start the new run |
63+
64+
## Managing schedules
65+
66+
### Pause and resume
67+
68+
```php
69+
ScheduleManager::pause($schedule);
70+
71+
// The schedule will not trigger while paused.
72+
73+
ScheduleManager::resume($schedule);
74+
// next_run_at is recalculated from now.
75+
```
76+
77+
### Update
78+
79+
```php
80+
ScheduleManager::update(
81+
$schedule,
82+
cronExpression: '30 3 * * *',
83+
timezone: 'America/Chicago',
84+
overlapPolicy: ScheduleOverlapPolicy::AllowAll,
85+
notes: 'Moved to 3:30 AM CT.',
86+
);
87+
```
88+
89+
Updating the cron expression or timezone recalculates `next_run_at`.
90+
91+
### Delete
92+
93+
```php
94+
ScheduleManager::delete($schedule);
95+
```
96+
97+
Deleting is soft — the row remains with status `deleted` and a `deleted_at` timestamp. A deleted schedule cannot be paused, resumed, updated, or triggered.
98+
99+
### Describe
100+
101+
```php
102+
$description = ScheduleManager::describe($schedule);
103+
104+
$description->scheduleId; // 'daily-invoice-sync'
105+
$description->status; // ScheduleStatus::Active
106+
$description->cronExpression; // '0 2 * * *'
107+
$description->totalRuns; // 47
108+
$description->nextRunAt; // DateTimeInterface
109+
$description->toArray(); // full array representation
110+
```
111+
112+
### Find by schedule ID
113+
114+
```php
115+
$schedule = ScheduleManager::findByScheduleId('daily-invoice-sync');
116+
```
117+
118+
## Triggering schedules
119+
120+
### Manual trigger
121+
122+
```php
123+
$instanceId = ScheduleManager::trigger($schedule);
124+
```
125+
126+
This immediately evaluates the overlap policy and, if allowed, starts a new workflow run. Returns the instance ID of the started workflow, or `null` if the trigger was skipped.
127+
128+
### Tick (evaluate all due schedules)
129+
130+
```php
131+
$results = ScheduleManager::tick();
132+
133+
// Returns: [['schedule_id' => '...', 'instance_id' => '...|null'], ...]
134+
```
135+
136+
`tick()` finds all active schedules whose `next_run_at` is in the past and triggers them in order. After each trigger, `next_run_at` advances to the next cron occurrence.
137+
138+
### Artisan command
139+
140+
Run a single tick from the command line:
141+
142+
```bash
143+
php artisan workflow:v2:schedule-tick
144+
php artisan workflow:v2:schedule-tick --json
145+
```
146+
147+
To evaluate schedules continuously, call this command from Laravel's task scheduler:
148+
149+
```php
150+
// app/Console/Kernel.php
151+
$schedule->command('workflow:v2:schedule-tick')->everyMinute();
152+
```
153+
154+
## Max runs
155+
156+
When `maxRuns` is set, the schedule tracks `remaining_actions`. After the last allowed trigger, the schedule is automatically soft-deleted.
157+
158+
```php
159+
$schedule = ScheduleManager::create(
160+
scheduleId: 'one-shot-retry',
161+
workflowClass: RetryWorkflow::class,
162+
cronExpression: '*/5 * * * *',
163+
maxRuns: 3,
164+
);
165+
166+
// After 3 triggers, the schedule status becomes 'deleted'.
167+
```
168+
169+
## History event types
170+
171+
Schedule lifecycle operations produce typed history events for auditability:
172+
173+
- `ScheduleCreated` — schedule was created
174+
- `SchedulePaused` — schedule was paused
175+
- `ScheduleResumed` — schedule was resumed
176+
- `ScheduleUpdated` — schedule cron, timezone, or policy was changed
177+
- `ScheduleTriggered` — a workflow run was started from the schedule
178+
- `ScheduleDeleted` — schedule was soft-deleted
179+
- `ScheduleTriggerSkipped` — a trigger was skipped due to overlap policy or exhausted actions
180+
181+
## Database
182+
183+
The schedule table (`workflow_schedules`) is created by migration `2026_04_14_000157`. The model class is configurable via `workflows.v2.schedule_model`.

0 commit comments

Comments
 (0)