Skip to content

Commit 285b3bb

Browse files
durable-workflow.github.io: update v2 changes
1 parent c773d33 commit 285b3bb

File tree

1 file changed

+43
-0
lines changed

1 file changed

+43
-0
lines changed

docs/failures-and-recovery.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,23 @@ The background task watchdog also scans for non-terminal runs with expired deadl
116116

117117
Waterline surfaces `failure_category` in the exceptions table as a dedicated **Category** column and in timeline failure detail entries. History exports include `failure_category` in the `failures[*]` array. Older failure rows that predate this classification have `failure_category = null` and are treated as unclassified in projections.
118118

119+
To backfill `failure_category` and `non_retryable` on older failure rows:
120+
121+
```bash
122+
# Preview what would be updated
123+
php artisan workflow:v2:backfill-failure-categories --dry-run
124+
125+
# Backfill all unclassified failures
126+
php artisan workflow:v2:backfill-failure-categories
127+
128+
# Scope to a specific run or instance
129+
php artisan workflow:v2:backfill-failure-categories --run-id=01HXYZ...
130+
php artisan workflow:v2:backfill-failure-categories --instance-id=order-123
131+
132+
# JSON output for scripting
133+
php artisan workflow:v2:backfill-failure-categories --json
134+
```
135+
119136
## Activity Retries
120137

121138
`Workflow\V2\Activity` defaults to `$tries = 1`, so an activity failure is sent back to the workflow immediately unless the activity opts into retry attempts.
@@ -146,6 +163,32 @@ The retry task records `retry_of_task_id`, `retry_after_attempt_id`, `retry_afte
146163

147164
`Workflow\Exceptions\NonRetryableExceptionContract` still short-circuits the retry policy: throwing a non-retryable exception fails the activity execution immediately and resumes the workflow with the exception.
148165

166+
### Non-retryable failure markers
167+
168+
When an activity or workflow throws an exception that implements `Workflow\Exceptions\NonRetryableExceptionContract`, the engine records a `non_retryable = true` flag on the `WorkflowFailure` row and in the typed history event payload (`ActivityFailed`, `WorkflowFailed`, `UpdateCompleted`). This durable marker communicates to operators, external workers, and tooling that the failure is permanent — retrying the same operation will not succeed.
169+
170+
The flag flows through the full visibility stack:
171+
172+
- **Failure rows**: `workflow_failures.non_retryable` boolean column.
173+
- **History events**: `non_retryable` field in the typed event payload.
174+
- **Failure snapshots**: `non_retryable` included in `FailureSnapshots::forRun()`.
175+
- **Run detail view**: `non_retryable` in the exceptions array.
176+
- **Timeline entries**: `non_retryable` in failure detail metadata.
177+
- **History exports**: `non_retryable` in the `failures[*]` array.
178+
- **Waterline**: a "non-retryable" badge next to the failure category in the exceptions table and timeline.
179+
- **External worker bridge**: the `complete()` command payload accepts `non_retryable` so external workflow workers can report non-retryable failures without requiring the host process to resolve the throwable class.
180+
181+
For failures that do not implement the contract, `non_retryable` is `false` by default. The `php artisan workflow:v2:backfill-failure-categories` command can detect and backfill the flag for older failure rows whose recorded exception class implements `NonRetryableExceptionContract` in the current codebase.
182+
183+
```php
184+
use Workflow\Exceptions\NonRetryableExceptionContract;
185+
186+
class PaymentDeclinedException extends \RuntimeException implements NonRetryableExceptionContract
187+
{
188+
// This failure will be marked as non-retryable in the durable record.
189+
}
190+
```
191+
149192
## Workflow-Level Retry
150193

151194
Durable Workflow v2 does **not** support automatic workflow-level retry. When a workflow run fails — whether from an unhandled exception, a structural limit, or a timeout — the run is terminal. The engine does not automatically start a new run of the same workflow instance.

0 commit comments

Comments
 (0)