Skip to content

Commit 68e5e6b

Browse files
durable-workflow.github.io: update v2 changes
1 parent 9e23e35 commit 68e5e6b

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed

docs/defining-workflows/starting-workflows.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,46 @@ Visibility labels are exact-match strings for operator filtering in Waterline, n
7575

7676
`memo` is JSON-like metadata for selected-run detail and history export, not a list-filter or run-summary search field. Top-level and nested memo object keys must be non-empty strings up to 64 characters, and memo values may be scalars, `null`, arrays, or nested objects.
7777

78+
### Search Attributes
79+
80+
Search attributes are indexed scalar metadata for operator filtering and fleet visibility. Unlike memo, search attributes are surfaced in run list views and can be used for visibility filtering in Waterline:
81+
82+
```php
83+
$workflow->start(
84+
$orderId,
85+
StartOptions::withVisibility(
86+
businessKey: 'order-123',
87+
labels: ['tenant' => 'acme'],
88+
)->withSearchAttributes([
89+
'priority' => 'high',
90+
'status' => 'pending',
91+
'amount' => '99.50',
92+
]),
93+
);
94+
```
95+
96+
Search attribute keys follow the same rules as visibility label keys: letters, numbers, `.`, `_`, `-`, and `:`, up to 64 characters. Values must be scalars or `null`. Boolean values are cast to `"1"` or `"0"`, and `null` values are silently dropped. Empty string values after casting are also dropped.
97+
98+
Search attributes can be upserted during workflow execution using `upsertSearchAttributes()`, which merges the new attributes into the existing set.
99+
100+
### Execution and Run Timeouts
101+
102+
`StartOptions` also supports execution-level and run-level timeouts:
103+
104+
```php
105+
$workflow->start(
106+
$orderId,
107+
(new StartOptions())
108+
->withExecutionTimeout(86400) // 24 hours across all runs
109+
->withRunTimeout(3600), // 1 hour per individual run
110+
);
111+
```
112+
113+
- **Execution timeout** spans the entire workflow instance lifecycle, including retries and continue-as-new runs. Once the execution deadline passes, the engine will not schedule further workflow tasks.
114+
- **Run timeout** applies to the current run only. It resets when a workflow continues as new.
115+
116+
Both timeouts must be at least 1 second. Pass `null` (the default) to leave the timeout unlimited.
117+
78118
## Workflow Type
79119

80120
The durable `workflow_type` for that instance comes from either:

docs/testing.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,96 @@ WorkflowStub::assertUpdateSent(
230230
);
231231
```
232232

233+
### Testing Start Outcomes and Duplicate-Start Policy
234+
235+
Use `attemptStart()` to verify duplicate-start behavior without throwing exceptions. The `StartResult` exposes typed outcome helpers:
236+
237+
```php
238+
use Workflow\V2\StartOptions;
239+
use Workflow\V2\WorkflowStub;
240+
241+
public function testRejectDuplicateStart(): void
242+
{
243+
WorkflowStub::fake();
244+
WorkflowStub::mock(MyActivity::class, 'result');
245+
246+
$workflow = WorkflowStub::make(MyWorkflow::class, 'order-123');
247+
248+
$first = $workflow->start('Taylor');
249+
$this->assertTrue($first->startedNew());
250+
251+
$second = WorkflowStub::load('order-123');
252+
$duplicate = $second->attemptStart('Taylor');
253+
254+
$this->assertTrue($duplicate->rejected());
255+
$this->assertTrue($duplicate->rejectedDuplicate());
256+
$this->assertSame('instance_already_started', $duplicate->rejectionReason());
257+
}
258+
```
259+
260+
To test the return-existing-active policy, pass `StartOptions::returnExistingActive()`:
261+
262+
```php
263+
public function testReturnExistingActiveStart(): void
264+
{
265+
WorkflowStub::fake();
266+
267+
$workflow = WorkflowStub::make(MySignalWorkflow::class, 'order-123');
268+
$workflow->start();
269+
270+
$this->assertSame('waiting', $workflow->refresh()->status());
271+
272+
$second = WorkflowStub::load('order-123');
273+
$result = $second->attemptStart(StartOptions::returnExistingActive());
274+
275+
$this->assertTrue($result->accepted());
276+
$this->assertTrue($result->returnedExistingActive());
277+
$this->assertSame($workflow->runId(), $result->runId());
278+
}
279+
```
280+
281+
Both policies record durable command and history events, so you can also assert on `WorkflowCommand` and `WorkflowHistoryEvent` rows for deeper verification.
282+
283+
### Testing History Budget
284+
285+
The `HistoryBudget` fields (`history_event_count`, `history_size_bytes`, `continue_as_new_recommended`) are surfaced on the run summary projection and are available through the `RunDetailView`. In your workflow code, the `Workflow` base class exposes these as `$this->historyEventCount()`, `$this->historySizeBytes()`, and `$this->continueAsNewRecommended()`:
286+
287+
```php
288+
use Workflow\V2\Workflow;
289+
290+
final class LongRunningWorkflow extends Workflow
291+
{
292+
public function handle(): void
293+
{
294+
while (true) {
295+
// ... process work ...
296+
297+
if ($this->continueAsNewRecommended()) {
298+
$this->continueAsNew($this->carryForwardState());
299+
}
300+
}
301+
}
302+
}
303+
```
304+
305+
To test history budget thresholds, configure the thresholds low and verify the recommendation:
306+
307+
```php
308+
public function testHistoryBudgetRecommendsContinueAsNew(): void
309+
{
310+
config()->set('workflows.v2.history_budget.continue_as_new_event_threshold', 5);
311+
312+
WorkflowStub::fake();
313+
314+
// Run a workflow that produces enough history events to trip the threshold
315+
$workflow = WorkflowStub::make(ManyActivitiesWorkflow::class, 'budget-test');
316+
$workflow->start();
317+
318+
$summary = $workflow->summary();
319+
$this->assertTrue($summary->continue_as_new_recommended);
320+
}
321+
```
322+
233323
## Legacy `Workflow\WorkflowStub`
234324

235325
### Workflows

0 commit comments

Comments
 (0)