You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
That instance-scoped selector is useful when another system already stored the public instance id and later wants to pin a historical run without changing the outer workflow address.
42
42
43
-
All three loaders accept an optional `namespace` parameter. When provided, loading is scoped to the given namespace at the query level — a workflow belonging to a different namespace will not be found. See [Namespace Scoping](/configuration/options#namespace-scoping) for details.
43
+
All three loaders accept an optional `namespace` parameter. When provided, loading is scoped to the given namespace at the query level — a workflow belonging to a different namespace will not be found. See [Namespace](../configuration/options.md#namespace) for details.
44
44
45
45
Instance-scoped current-run selection no longer trusts only the mutable `workflow_instances.current_run_id` pointer. `load($instanceId)`, `currentRunId()`, Waterline instance routes, and other instance-targeted current-run actions resolve the newest durable run in the instance chain, so continue-as-new navigation still lands on the right run even if that pointer is temporarily stale or null.
Copy file name to clipboardExpand all lines: docs/features/timeouts.md
+69-2Lines changed: 69 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -104,11 +104,78 @@ When a workflow continues as new:
104
104
105
105
This means the execution timeout always measures from the original start, while each new run gets its own fresh run-timeout window.
106
106
107
+
## Activity timeouts
108
+
109
+
Activity timeouts let you bound how long individual activity executions are allowed to take. There are four activity timeout scopes:
110
+
111
+
-**Schedule-to-start** — caps the time from scheduling to the first worker claim. Enforced while the activity is `Pending`.
112
+
-**Start-to-close** — caps the time from when a worker claims the activity to when it must complete. Resets on each retry attempt.
113
+
-**Schedule-to-close** — caps the total wall-clock time from scheduling to completion across all retry attempts. This is always terminal — retrying would not help because the overall deadline has passed.
114
+
-**Heartbeat** — caps the time between heartbeats. For long-running activities that call `$this->heartbeat()`, the engine requires a heartbeat within the configured interval or the activity is considered unresponsive.
115
+
116
+
All are optional and can be combined. Configure them through `ActivityOptions` when calling an activity:
117
+
118
+
```php
119
+
use function Workflow\V2\activity;
120
+
use Workflow\V2\Support\ActivityOptions;
121
+
122
+
$result = activity(
123
+
LongRunningActivity::class,
124
+
new ActivityOptions(
125
+
scheduleToStartTimeout: 30, // must be claimed within 30s
126
+
startToCloseTimeout: 300, // each attempt has 5 minutes
127
+
scheduleToCloseTimeout: 600, // total 10 minutes across all retries
128
+
heartbeatTimeout: 15, // must heartbeat every 15 seconds
129
+
maxAttempts: 3,
130
+
),
131
+
$input,
132
+
);
133
+
```
134
+
135
+
### Schedule-to-start timeout
136
+
137
+
The deadline is computed when the activity is scheduled. If no worker claims the activity before the deadline, the `TaskWatchdog` enforces the timeout. If retry attempts remain, a new activity task is scheduled with the snapped backoff; otherwise a terminal `ActivityTimedOut` history event is recorded and the workflow is woken.
138
+
139
+
### Start-to-close timeout
140
+
141
+
The deadline is computed when a worker claims the activity task. Each retry gets a fresh deadline. If the activity does not complete before the deadline, the current attempt is closed and the same retry-or-terminal decision applies.
142
+
143
+
### Schedule-to-close timeout
144
+
145
+
The deadline is computed once at scheduling time and never resets. When it expires, the activity is immediately failed as terminal — even if retry attempts remain, because the total allowed wall-clock time has passed. This is useful for bounding the overall cost of a flaky activity that might otherwise retry indefinitely within its per-attempt limits.
146
+
147
+
### Heartbeat timeout
148
+
149
+
The initial deadline is computed when a worker claims the activity task. Each successful `$this->heartbeat()` call extends the deadline by the configured interval. If the activity does not call `heartbeat()` before the deadline expires, the engine assumes the worker is unresponsive. If retry attempts remain, a new attempt is scheduled; otherwise a terminal timeout is recorded.
150
+
151
+
```php
152
+
use Workflow\V2\Activity;
153
+
154
+
class LongRunningActivity extends Activity
155
+
{
156
+
public function handle($input)
157
+
{
158
+
foreach ($items as $item) {
159
+
$this->heartbeat(['processed' => $count]);
160
+
// ... process item ...
161
+
}
162
+
}
163
+
}
164
+
```
165
+
166
+
### Enforcement
167
+
168
+
Activity timeouts are enforced by the `TaskWatchdog` on each worker-loop pass. The watchdog scans for executions whose deadline columns have passed and delegates to `ActivityTimeoutEnforcer`. Each enforcement records:
169
+
170
+
- A terminal `ActivityTimedOut` history event with `timeout_kind` set to `schedule_to_start`, `start_to_close`, `schedule_to_close`, or `heartbeat`
171
+
- A `WorkflowFailure` row with `failure_category = timeout` and `propagation_kind = timeout`
172
+
- A workflow resume task to wake the parent workflow so it can observe the failure
173
+
174
+
Waterline displays the retry policy including all configured timeout types in the activity detail view. The timeline shows the timeout kind in the activity timed-out event message.
175
+
107
176
### What is not yet covered
108
177
109
178
The following are planned but not yet implemented:
0 commit comments