Skip to content

Commit 670ef05

Browse files
durable-workflow.github.io: update v2 changes
1 parent 3f61c71 commit 670ef05

File tree

1 file changed

+93
-0
lines changed

1 file changed

+93
-0
lines changed

docs/features/schedules.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,76 @@ The `scheduleId` is a unique, user-chosen identifier for the schedule. Each trig
4848
| `connection` | `string\|null` | `null` | Queue connection for triggered runs (overrides the workflow class default) |
4949
| `queue` | `string\|null` | `null` | Queue name for triggered runs (overrides the workflow class default) |
5050
| `notes` | `string\|null` | `null` | Free-form operator notes |
51+
| `namespace` | `string\|null` | `null` | Namespace for the schedule (defaults to the configured `workflows.v2.namespace` or `'default'`) |
52+
53+
## Interval-based schedules
54+
55+
In addition to cron expressions, schedules support interval-based firing using ISO 8601 duration syntax. Use `ScheduleManager::createFromSpec()` for full control over the schedule spec:
56+
57+
```php
58+
use Workflow\V2\Support\ScheduleManager;
59+
60+
$schedule = ScheduleManager::createFromSpec(
61+
scheduleId: 'health-check-30m',
62+
spec: [
63+
'intervals' => [
64+
['every' => 'PT30M'],
65+
],
66+
],
67+
action: [
68+
'workflow_type' => 'health-check',
69+
'workflow_class' => HealthCheckWorkflow::class,
70+
'input' => ['region' => 'us-east-1'],
71+
],
72+
);
73+
```
74+
75+
### Interval spec fields
76+
77+
| Field | Type | Description |
78+
|---|---|---|
79+
| `every` | `string` | ISO 8601 duration (e.g., `PT30M` for 30 minutes, `PT1H` for 1 hour, `P1D` for 1 day) |
80+
| `offset` | `string\|null` | Phase offset as ISO 8601 duration — shifts the alignment point of the interval |
81+
82+
The offset parameter controls where in the interval cycle the schedule fires. For example, an hourly interval with a 5-minute offset fires at `:05`, `:05+1h`, etc.:
83+
84+
```php
85+
$schedule = ScheduleManager::createFromSpec(
86+
scheduleId: 'offset-hourly',
87+
spec: [
88+
'intervals' => [
89+
['every' => 'PT1H', 'offset' => 'PT5M'],
90+
],
91+
],
92+
action: [
93+
'workflow_type' => 'sync-workflow',
94+
'workflow_class' => SyncWorkflow::class,
95+
'input' => [],
96+
],
97+
);
98+
```
99+
100+
### Mixed cron and interval specs
101+
102+
A single schedule can combine cron expressions and intervals. The engine evaluates all specs and uses the earliest upcoming fire time:
103+
104+
```php
105+
$schedule = ScheduleManager::createFromSpec(
106+
scheduleId: 'mixed-schedule',
107+
spec: [
108+
'cron_expressions' => ['0 12 * * *'], // noon daily
109+
'intervals' => [['every' => 'PT6H']], // every 6 hours
110+
'timezone' => 'America/Chicago',
111+
],
112+
action: [
113+
'workflow_type' => 'report-workflow',
114+
'workflow_class' => ReportWorkflow::class,
115+
'input' => [],
116+
],
117+
);
118+
```
119+
120+
The `createFromSpec` method accepts all the same lifecycle parameters as `create()` (`overlapPolicy`, `jitterSeconds`, `maxRuns`, `connection`, `queue`, `namespace`, etc.) as separate named arguments.
51121

52122
## Overlap policies
53123

@@ -103,13 +173,16 @@ Deleting is soft — the row remains with status `deleted` and a `deleted_at` ti
103173
$description = ScheduleManager::describe($schedule);
104174

105175
$description->scheduleId; // 'daily-invoice-sync'
176+
$description->namespace; // 'default'
106177
$description->status; // ScheduleStatus::Active
107178
$description->spec; // ['cron_expressions' => ['0 2 * * *'], 'timezone' => 'America/New_York']
108179
$description->overlapPolicy; // ScheduleOverlapPolicy::Skip
109180
$description->firesCount; // 47
110181
$description->nextFireAt; // DateTimeInterface|null
111182
$description->lastFiredAt; // DateTimeInterface|null
112183
$description->latestInstanceId; // 'schedule:daily-invoice-sync:...'
184+
$description->jitterSeconds; // 0
185+
$description->note; // 'Runs every night at 2 AM ET.'
113186
$description->toArray(); // full array representation
114187
```
115188

@@ -272,6 +345,26 @@ When a trigger is skipped (due to overlap policy, non-triggerable status, or exh
272345

273346
These fields are included in `ScheduleManager::describe()` and the Waterline schedule detail API.
274347

348+
## Namespace scoping
349+
350+
Schedules belong to a namespace. When `namespace` is passed to `create()` or `createFromSpec()`, the schedule is scoped to that namespace. When omitted, the schedule inherits the configured `workflows.v2.namespace` (defaulting to `'default'`).
351+
352+
Schedule IDs are unique within a namespace — the same `scheduleId` can exist in different namespaces without conflict.
353+
354+
```php
355+
$schedule = ScheduleManager::create(
356+
scheduleId: 'daily-sync',
357+
workflowClass: SyncWorkflow::class,
358+
cronExpression: '0 2 * * *',
359+
namespace: 'billing',
360+
);
361+
362+
// Find by schedule ID within a namespace:
363+
$found = ScheduleManager::findByScheduleId('daily-sync', namespace: 'billing');
364+
```
365+
366+
When Waterline is configured with a namespace (`waterline.namespace`), the schedule list and detail endpoints automatically scope to that namespace. This ensures multi-tenant deployments show only the schedules belonging to the operator's namespace.
367+
275368
## Database
276369

277370
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)