Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions docs/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,12 @@ The embedded package and the service-mode SDK have separate support contracts.

| Package line | PHP | Laravel |
| --- | --- | --- |
| Current embedded Workflow 2.26 | PHP 8.2+ in 8.x; PHP 8.3+ for Laravel 13 | `^12.69.0` or `^13.30.0` |
| Current embedded Workflow 2.27 | PHP 8.2+ in 8.x; PHP 8.3+ for Laravel 13 | `^12.69.0` or `^13.30.0` |
| Embedded Workflow 2.20.5 | PHP `^8.1`, subject to Laravel's own minimum | `^10.50.4`, `^11.56.1`, `^12.69.0` or `^13.30.0` |
| Published PHP SDK 3.14 | Bare SDK PHP `^8.1`; the Laravel bridge needs PHP `^8.2`, and PHP 8.3+ for Laravel 13 | `^12.69.0` or `^13.30.0` |
| PHP SDK 3.9.5 | Bare SDK PHP `^8.1`; the Laravel bridge also follows Laravel's PHP minimum | `^10.50.4`, `^11.56.1`, `^12.69.0` or `^13.30.0` |

The [published embedded upgrade contract](https://github.com/durable-workflow/workflow/blob/2.26.0/resources/laravel-embedded-upgrade-contract.json)
The [published embedded upgrade contract](https://github.com/durable-workflow/workflow/blob/2.27.0/resources/laravel-embedded-upgrade-contract.json)
and each release's `composer.json` are the package authorities. The current
embedded release was verified on Laravel 12 with PHP 8.2–8.4 and Laravel 13 with
PHP 8.3–8.4. Its PHP 8.1 and Laravel 10/11 support has been removed. This does
Expand Down Expand Up @@ -193,6 +193,13 @@ See the release notes for
[2.25.0](https://github.com/durable-workflow/workflow/releases/tag/2.25.0) and
[2.26.0](https://github.com/durable-workflow/workflow/releases/tag/2.26.0).

Workflow 2.27 keeps the 2.21 support floors and the embedded worker protocol `1.22`, and adds no database migration. Changes to check before upgrading:

- From 2.27, an embedded app repairs and runs work only in its own namespaces. If the app starts, schedules or signals workflows in namespaces other than `DW_V2_NAMESPACE`, list them all in `DW_V2_NAMESPACES`. A process that serves every namespace sets `DW_V2_ALL_NAMESPACES=true`. See [Which namespaces an app runs](./configuration/options.md#namespace-allowlist).
- From 2.27, closing a child costs the parent the same work however many siblings it has. See [Cost of wide fan-out](./features/child-workflows.md#fan-out-cost).

See the [2.27.0 release notes](https://github.com/durable-workflow/workflow/releases/tag/2.27.0).

## Stability Levels

Every public surface in Durable Workflow carries exactly one of these
Expand Down
15 changes: 15 additions & 0 deletions docs/configuration/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,21 @@ $controlPlane->start('order-processing', 'order-12345', [

When no namespace is configured and none is passed explicitly, new instances retain a `null` namespace. A namespace-scoped observer does not include those runs. Changing configuration does not reassign existing instances. Starting an existing instance through a different namespace is rejected.

### Which namespaces an app runs {#namespace-allowlist}

From Workflow 2.27, an embedded app repairs and runs work only in the namespaces it serves. This covers the watchdog, task repair, run and activity deadlines, `workflow:v2:schedule-tick`, and the queued workflow, activity, timer and service-call jobs. A queued job for another namespace is skipped before it has any effect, and a warning is logged.

```env
DW_V2_NAMESPACE=production
DW_V2_NAMESPACES=production,billing
```

- When `DW_V2_NAMESPACES` is unset, the app serves exactly `DW_V2_NAMESPACE`, so a single-namespace app needs no change.
- When it is set, the app serves exactly the listed namespaces, so include `DW_V2_NAMESPACE` itself. `default` names the unscoped namespace. An empty value serves none, and `*` is refused.
- A process that repairs or ticks every namespace in the database, such as a standalone server, sets `DW_V2_ALL_NAMESPACES=true` instead.

List every namespace the app starts, schedules or signals workflows in. Otherwise their tasks and schedules wait for an app that serves them. The allowlist keeps one app from running another app's work with the wrong code or credentials. It isn't tenant isolation: apps that share a database can still read each other's rows. Refused work is counted in `namespace_scope_refusals`; see [Monitoring](../monitoring.md#stuck-run-metrics).

### Waterline namespace scoping

When Waterline is deployed against a shared database with multiple namespaces, set `WATERLINE_NAMESPACE` to restrict all list views to one namespace:
Expand Down
11 changes: 11 additions & 0 deletions docs/features/child-workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@ That child-only form is still useful when every parallel member is a child workf

If you need to combine child workflows with activities in the same fan-in step, use one mixed `all([...])` barrier instead. The child waits and activity waits share the same `parallel_group_id`, and Waterline labels that shared barrier as `parallel_group_kind = mixed`. If one mixed or child-only subgroup sits inside a larger `all([...])`, the innermost group still drives `parallel_group_id` while `parallel_group_path` preserves the full outer-to-inner path.

### Cost of wide fan-out {#fan-out-cost}

Each child close locks the parent run while it records the child's outcome, checks the group barrier and updates the parent's projections. In Workflow 2.27 and later, that work doesn't grow with the number of siblings: a close reads no other child's history and runs a fixed number of queries. Closes of one parent's children still take turns on the parent's lock, and the parent can't run its own workflow task while a close holds it.

Before 2.27, each close also re-read every sibling's history and run, so closing all children of a wide fan-out cost work that grew with the square of the number of children.

For fan-outs in the hundreds, or close to the default limit of 1,000 pending children (`pending_child_count`, see [Structural limits](../constraints/structural-limits.md)), split the work:

- **Batches:** start a group of children, wait for it with `all([...])`, then start the next group.
- **A tree:** let each child start its own children, so no single parent supervises more than a few hundred at once.

## Identity Contract

- `child()->id()` and Waterline `target_name` refer to the child workflow instance id, which stays stable across that child instance's run chain.
Expand Down
4 changes: 3 additions & 1 deletion docs/monitoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ Runs with workflow-task trouble have `task_problem = true`. Filter run lists on
- `active` (Task Problem): workflow-task work is missing, retrying or cannot be delivered right now.
- `history` (Task History): the run needed workflow-task repair or replay recovery earlier.

### Metrics and health
### Metrics and health {#stuck-run-metrics}

Embedded operator metrics (`operator_metrics` in Waterline stats) count open runs in each problem state. Closed runs are not counted.

Expand All @@ -175,6 +175,8 @@ The advisory `replay_blocked_runs` health check (category `correctness`) reports

Native Server reports `replay_blocked` and the `replay_blocked_runs` check from 3.6.0.

From Workflow 2.27, `operator_metrics.namespace_scope_refusals` counts work the app's [namespace allowlist](./configuration/options.md#namespace-allowlist) refused in the last 15 minutes, by namespace and path. `job` counts skipped queued jobs. `repair`, `timeout` and `schedule_tick` count repair candidates, expired deadlines and due schedules left in a namespace outside the allowlist for more than two minutes. The advisory `namespace_scope_refusals` health check warns while refusals keep arriving and clears 15 minutes after the last one. The counts live in the default cache store, so use a shared store (Redis, database or Memcached) so that the web process serving health sees the workers' counts.

### Server workflow-task failures

Server reports failed workflow tasks in `GET /api/system/metrics` under `metrics.dw_workflow_task_consecutive_failures`:
Expand Down
11 changes: 9 additions & 2 deletions i18n/de/docusaurus-plugin-content-docs/current/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,12 @@ Das eingebettete Paket und das Service-Modus-SDK haben eigene Supportverträge.

| Paketlinie | PHP | Laravel |
| --- | --- | --- |
| Aktuelles eingebettetes Workflow 2.26 | PHP 8.2+ in 8.x; PHP 8.3+ für Laravel 13 | `^12.69.0` oder `^13.30.0` |
| Aktuelles eingebettetes Workflow 2.27 | PHP 8.2+ in 8.x; PHP 8.3+ für Laravel 13 | `^12.69.0` oder `^13.30.0` |
| Eingebettetes Workflow 2.20.5 | PHP `^8.1`, zusätzlich gilt das Framework-Minimum | `^10.50.4`, `^11.56.1`, `^12.69.0` oder `^13.30.0` |
| Veröffentlichtes PHP-SDK 3.14 | Reines SDK PHP `^8.1`; die Laravel-Bridge benötigt PHP `^8.2` und für Laravel 13 PHP 8.3+ | `^12.69.0` oder `^13.30.0` |
| PHP-SDK 3.9.5 | Reines SDK PHP `^8.1`; die Laravel-Bridge beachtet ebenfalls das Framework-Minimum | `^10.50.4`, `^11.56.1`, `^12.69.0` oder `^13.30.0` |

Maßgeblich sind der [veröffentlichte eingebettete Upgrade-Vertrag](https://github.com/durable-workflow/workflow/blob/2.26.0/resources/laravel-embedded-upgrade-contract.json)
Maßgeblich sind der [veröffentlichte eingebettete Upgrade-Vertrag](https://github.com/durable-workflow/workflow/blob/2.27.0/resources/laravel-embedded-upgrade-contract.json)
und die jeweilige `composer.json`. Das aktuelle eingebettete Release wurde mit
Laravel 12/PHP 8.2–8.4 und Laravel 13/PHP 8.3–8.4 geprüft. Es entfernt PHP 8.1
und Laravel 10/11. Die [historische stabile v1-Anleitung](/docs/1.x/installation/)
Expand Down Expand Up @@ -124,6 +124,13 @@ Workflow 2.25 und 2.26 behalten die Mindestanforderungen von 2.21 und das eingeb

Siehe die Versionshinweise zu [2.25.0](https://github.com/durable-workflow/workflow/releases/tag/2.25.0) und [2.26.0](https://github.com/durable-workflow/workflow/releases/tag/2.26.0).

Workflow 2.27 behält die Mindestanforderungen von 2.21 und das eingebettete Worker-Protokoll `1.22` bei und fügt keine Datenbankmigration hinzu. Vor dem Upgrade zu prüfende Änderungen:

- Ab 2.27 repariert und führt eine eingebettete App Arbeit nur in ihren eigenen Namespaces aus. Startet, plant oder signalisiert die App Workflows in anderen Namespaces als `DW_V2_NAMESPACE`, führen Sie alle in `DW_V2_NAMESPACES` auf. Ein Prozess, der jeden Namespace bedient, setzt `DW_V2_ALL_NAMESPACES=true`. Siehe [Welche Namespaces eine App ausführt](./configuration/options.md#namespace-allowlist).
- Ab 2.27 kostet der Abschluss eines Kindes den Elternworkflow gleich viel Arbeit, egal wie viele Geschwister es gibt. Siehe [Kosten breiter Verzweigungen](./features/child-workflows.md#fan-out-cost).

Siehe die [Versionshinweise zu 2.27.0](https://github.com/durable-workflow/workflow/releases/tag/2.27.0).

## Stabilitätsstufen {#stability-levels}

Jede öffentliche Durable-Workflow-Schnittstelle trägt genau eine dieser Stabilitätsstufen. Die Stufen sind explizit: Eine nicht klassifizierte Schnittstelle ist nicht öffentlich.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,21 @@ Beobachter schließt diese Ausführungen nicht ein. Konfigurationsänderungen
weisen vorhandene Instanzen nicht neu zu. Der Start einer vorhandenen Instanz
über einen anderen Namespace wird abgelehnt.

### Welche Namespaces eine App ausführt {#namespace-allowlist}

Ab Workflow 2.27 repariert und führt eine eingebettete App Arbeit nur in den Namespaces aus, die sie bedient. Das betrifft den Watchdog, die Task-Reparatur, Lauf- und Aktivitätsfristen, `workflow:v2:schedule-tick` sowie die eingereihten Workflow-, Aktivitäts-, Timer- und Service-Call-Jobs. Ein eingereihter Job für einen anderen Namespace wird übersprungen, bevor er etwas bewirkt, und eine Warnung wird protokolliert.

```env
DW_V2_NAMESPACE=production
DW_V2_NAMESPACES=production,billing
```

- Ist `DW_V2_NAMESPACES` nicht gesetzt, bedient die App genau `DW_V2_NAMESPACE`. Eine App mit einem einzigen Namespace muss also nichts ändern.
- Ist die Variable gesetzt, bedient die App genau die aufgeführten Namespaces. Nehmen Sie daher auch `DW_V2_NAMESPACE` selbst auf. `default` bezeichnet den Namespace ohne Zuordnung. Ein leerer Wert bedient keinen, und `*` wird abgelehnt.
- Ein Prozess, der jeden Namespace der Datenbank repariert oder tickt, etwa ein eigenständiger Server, setzt stattdessen `DW_V2_ALL_NAMESPACES=true`.

Führen Sie jeden Namespace auf, in dem die App Workflows startet, plant oder signalisiert. Andernfalls warten deren Tasks und Zeitpläne auf eine App, die sie bedient. Die Allowlist verhindert, dass eine App die Arbeit einer anderen mit falschem Code oder falschen Zugangsdaten ausführt. Sie ist keine Mandantenisolation: Apps, die sich eine Datenbank teilen, können weiterhin die Zeilen der anderen lesen. Abgelehnte Arbeit wird in `namespace_scope_refusals` gezählt, siehe [Monitoring](../monitoring.md#stuck-run-metrics).

### Namespace-Beschränkung in Waterline {#waterline-namespace-scoping}

Wenn Waterline auf einer gemeinsamen Datenbank mit mehreren Namespaces
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,17 @@ in einer größeren `all([...])`-Gruppe, bestimmt weiterhin die innerste Gruppe
die `parallel_group_id`. `parallel_group_path` erhält den vollständigen Pfad
von außen nach innen.

### Kosten breiter Verzweigungen {#fan-out-cost}

Jeder Abschluss eines Kindes sperrt den Elternlauf, während er das Ergebnis des Kindes aufzeichnet, die Gruppenbarriere prüft und die Projektionen des Elternworkflows aktualisiert. Ab Workflow 2.27 wächst dieser Aufwand nicht mehr mit der Zahl der Geschwister: Ein Abschluss liest die Historie keines anderen Kindes und führt eine feste Zahl von Abfragen aus. Abschlüsse der Kinder eines Elternworkflows warten weiterhin nacheinander auf dessen Sperre, und der Elternworkflow kann seinen eigenen Workflow-Task nicht ausführen, solange ein Abschluss sie hält.

Vor 2.27 las jeder Abschluss zusätzlich die Historie und den Lauf jedes Geschwisters erneut. Alle Kinder einer breiten Verzweigung abzuschließen kostete daher Arbeit, die mit dem Quadrat der Kinderzahl wuchs.

Bei Verzweigungen in die Hunderte oder nahe am Standardlimit von 1.000 ausstehenden Kindern (`pending_child_count`, siehe [Strukturelle Grenzen](../constraints/structural-limits.md)) teilen Sie die Arbeit auf:

- **Stapel:** Starten Sie eine Gruppe von Kindern, warten Sie mit `all([...])` auf sie und starten Sie dann die nächste Gruppe.
- **Ein Baum:** Lassen Sie jedes Kind eigene Kinder starten, sodass kein einzelner Elternworkflow mehr als einige Hundert gleichzeitig beaufsichtigt.

## Identitätsvertrag {#identity-contract}

- `child()->id()` und Waterlines `target_name` beziehen sich auf die Workflow-Instanz-ID des Kindes, die über dessen Laufkette hinweg stabil bleibt.
Expand Down
2 changes: 2 additions & 0 deletions i18n/de/docusaurus-plugin-content-docs/current/monitoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ Die beratende Health-Prüfung `replay_blocked_runs` (Kategorie `correctness`) me

Der native Server meldet `replay_blocked` und die Prüfung `replay_blocked_runs` ab 3.6.0.

Ab Workflow 2.27 zählt `operator_metrics.namespace_scope_refusals` die Arbeit, die die [Namespace-Allowlist](./configuration/options.md#namespace-allowlist) der App in den letzten 15 Minuten abgelehnt hat, nach Namespace und Pfad. `job` zählt übersprungene eingereihte Jobs. `repair`, `timeout` und `schedule_tick` zählen Reparaturkandidaten, abgelaufene Fristen und fällige Zeitpläne, die länger als zwei Minuten in einem Namespace außerhalb der Allowlist liegen geblieben sind. Die beratende Health-Prüfung `namespace_scope_refusals` warnt, solange weiter Ablehnungen eintreffen, und wird 15 Minuten nach der letzten zurückgesetzt. Die Zähler liegen im Standard-Cache-Store. Verwenden Sie daher einen gemeinsamen Store (Redis, Datenbank oder Memcached), damit der Webprozess, der den Health-Status liefert, die Zähler der Worker sieht.

### Fehlgeschlagene Workflow-Tasks im Server {#workflow-task-failure-metric}

Der Server meldet fehlgeschlagene Workflow-Tasks in `GET /api/system/metrics` unter `metrics.dw_workflow_task_consecutive_failures`:
Expand Down
8 changes: 4 additions & 4 deletions i18n/de/source-hashes.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
"ai-agent-workflow-engine.md": "28016466b179adbabdff6467ce351100c9bee48529a8bec9df7ec22c8d786d4d",
"ai-assisted-development.md": "b246bb60585fb7a69bc534978f470f3f16e899e1f8f203ba0847d2d11b8a1ec3",
"capabilities.md": "d9a2bbc0d04adf8cb3f4c7f42d779a43aafab60791210305e7e476c0499528fc",
"compatibility.md": "ad5c8519be03ae1e7b6e5e03b05a886862fd67c603b241ecb5baf2be5cd59610",
"compatibility.md": "a1b478309cdb159ee2c15b3e1ced7aba7f8c5172ad7431b2e3db907b0c868147",
"configuration/customization-matrix.md": "3d115863eaee7852b541bcd01615f424cc19c5847c7cd7d75e5ae63bc3f5cc78",
"configuration/database-connection.md": "cc83ae77133236309506fa44d39bfd0c939c32a6154c0bd6099bc64c83bdaec9",
"configuration/ensuring-same-server.md": "6c665f37cc198c7b1a775a60a94aab5ff4e55383385e76f1834c7840559b512a",
"configuration/microservices.md": "46dc4afa07588b78829a7e3d259177ce0cf40861f0e31b88dbb6de3a83aa5488",
"configuration/options.md": "6a55ce6dd9828ad20a024d8d495b254bf1e1eea51dda3abd51ef4008e1dc9e9d",
"configuration/options.md": "42c40c50ecdf9fc6eace79d212229dfe0dac119c23137225f0ce353952fbd549",
"configuration/pruning-workflows.md": "6f6ee2be3bd185ffec764e86c3ed3cbd40c45f75d28671b4cd11226493e11afb",
"configuration/publishing-config.md": "c757acd749c0d2696a6e19484249c9298dd15970ce0cf2ae0e6d2dfd82c8b6d6",
"constraints/activity-constraints.md": "5a953d0e95e750b6c02d1c72b72199fb3ff4687540cebc56aa3b216cf667bb05",
Expand All @@ -32,7 +32,7 @@
"failures-and-recovery.md": "a240727af9539cca9cceb6ab3ddea545c95f21402813365442929ea05c09e70c",
"features/activity-execution-model.md": "e8c37520ae46a7efb30438f4fc06bf893c1df430cb1f92c48963697adaf09082",
"features/cancel-and-terminate.md": "cedd1e6c51dfa551e00edc1d39739ff1f67d558d2a0be92143f478cb6111536b",
"features/child-workflows.md": "278b634b8b48b94203309cdba27d6116897a2919cab19628add8cd85dadb08d3",
"features/child-workflows.md": "30dd8e1a131654d6320641446b6bd661f27cd591353a60d007574955749cf8fb",
"features/concurrency.md": "a7a3eb2274f7b14faf722494569b7663c07ab48afdf2e9225e58914b84cacbb4",
"features/condition-waits.md": "451261eda990c9591a0db69839d872184b5c64da6b537a582133095b8d4b599e",
"features/continue-as-new.md": "a5309192c685abeb5295071d423824ae2287e68d247a6661f18d3ab21f9bdc9c",
Expand Down Expand Up @@ -63,7 +63,7 @@
"laravel-adoption.md": "be0890619736ee2362ee96b6b1f5f397f10fb58cb845448ac15b437ac3062b36",
"mcp-workflows.md": "1c0b5bb7f2e5c7ca835dfdfa57bbe8f6624172c855f4e225057ac2377abf360e",
"migration.md": "7304a53786d5e45d3a639c33386e6558e855ee77982883a5e051ef5084b8453b",
"monitoring.md": "afbf43eacdd7ab017c75478a5ee4cb08745f7e4513799f949e57fc7f4d0fec2a",
"monitoring.md": "346b8a5207b7d69cea63dc9bfec95bf1911d3038a9d30ad06939b13130079279",
"operator-operating-envelope.md": "dc0cf13d8593e4ce2d8ec1912e45e61c9bf13f06abf7cc8c422379db5e3fa0ad",
"platform-conformance.md": "2eec7eba9a65c010e03c4b2eb78afe4dddd6fd8170218b3194c49e3777124c91",
"platform-protocol-specs.md": "ba9c69adfca7fb4a0dd982464b0ca69dd5a21de1e340edbe23b075ec0fb0c35c",
Expand Down
Loading
Loading