Skip to content
Draft
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
8 changes: 7 additions & 1 deletion docs/features/sagas.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,17 @@ Activities run at least once, so a forward call can take effect and still fail f

## Deadline-driven compensation

An engine [workflow timeout](./timeouts.md#timeout-outcome-and-cleanup) closes
With the default policy, an engine [workflow timeout](./timeouts.md#timeout-outcome-and-cleanup) closes
the run before workflow code can run compensation. A `catch` or `finally` block
does not change that behavior. Immediate `cancel()` and `terminate()` commands
also provide no cleanup window.

The separate [experimental timeout-cleanup option](./timeouts.md#timeout-cleanup)
can reserve time for an interruption-driven compensation handler inside the
original hard cap. It is embedded queue support with publication and installed
qualification still pending; it does not make compensation automatic or
guarantee an effect when workers are unavailable.

Use an earlier application deadline when timeout should undo completed work.
This embedded workflow reserves a resource, then waits up to 30 seconds for a
confirmation signal. Expiry resumes workflow code, which releases the
Expand Down
72 changes: 71 additions & 1 deletion docs/features/timeouts.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ The failure row stores `Workflow\V2\Exceptions\WorkflowTimeoutException` as the

### Timeout outcome and cleanup

A workflow deadline closes the run with status `failed` and
With the default hard-timeout policy, a workflow deadline closes the run with status `failed` and
`closed_reason = timed_out`. `WorkflowTimedOut` is the terminal history event.
The runtime records `WorkflowTimeoutException` as failure data. It does **not**
throw that exception into the workflow body or resume workflow code for cleanup.
Expand All @@ -151,6 +151,76 @@ cancellation request, and a cancellation shield does not extend an execution
or run deadline. See [Cancel and Terminate](./cancel-and-terminate.md) and
[Cooperative Cancellation](../polyglot/cancellation.md).

## Experimental deadline-triggered cleanup {#timeout-cleanup}

**Unreleased experimental embedded support.** This journey targets Workflow
2.29.0; publication and installed-package qualification are still pending.
The selected stable release keeps the default hard-timeout behavior above.

Before enabling this option, install the qualified release, run the application
migrations, and deploy compatible embedded queue workers. The new migration
adds `workflow_runs.timeout_cleanup_work_deadline_at`; it does not opt existing
runs in. Do not enable the option before the migration completes.

```bash
php artisan migrate
```

Reserve a finite part of the existing hard timeout when starting a run:

```php
use Workflow\V2\StartOptions;
use Workflow\V2\WorkflowStub;

WorkflowStub::make(MyWorkflow::class, 'order-123')->start(
$orderId,
StartOptions::rejectDuplicate()
->withExecutionTimeout(70)
->withTimeoutCleanup(10),
);
```

`timeoutCleanupSeconds:` is the constructor alternative. The allowance must be
1–86400 seconds, requires an execution or run timeout, and must be smaller than
every configured hard timeout. Passing `null` leaves the default policy.

In this example, ordinary work gets 60 seconds and cleanup may use the last
10 seconds inside the original 70-second hard cap. The allowance never adds
time after that cap. If both hard deadlines apply, the earlier one bounds
cleanup. Late detection, retries, worker replacement and lease renewal do not
refresh any deadline. Continue-as-new and redrive preserve the admitted
allowance and original execution clocks; each run derives its own run clocks.

At the work deadline, the run enters cleanup once and records
`WorkflowTimeoutCleanupStarted`. Compatible workflow code receives
`WorkflowTimeoutCleanupRequested`, a subtype of
`WorkflowCancellationRequestedException` with cancellation `cause: timeout`.
Handle that interruption to run registered saga compensation or protected
cleanup; `compensate()` uses its cancellation shield. The shield does not
extend the hard cap. See [Sagas](./sagas.md#deadline-driven-compensation).

Cleanup has a separate `timeout_cleanup.outcome`:

| Outcome | Meaning |
|---|---|
| `completed` | Workflow cleanup returned or rethrew the timeout interruption before its deadline. A returned value does not become the business result. |
| `failed` | Cleanup threw another exception; `failure_id` links the cleanup failure. |
| `expired` | The hard cap arrived before cleanup finished, including when no worker could run it. |
| `preempted` | Immediate `cancel()` or `terminate()` interrupted cleanup; the corresponding cancellation/termination outcome remains terminal. |

Completed, failed or expired cleanup still ends with business status `failed`,
`closed_reason = timed_out` and `WorkflowTimedOut`. Successful compensation does
not turn that timeout into a successful workflow. Inspect describe/run detail
and the retained history for both the business outcome and cleanup outcome.
Unavailable or delayed workers cannot guarantee an external cleanup effect;
keep compensation idempotent and reconcile uncertain effects in your application.

This increment supports embedded queue workers only. Poll-mode and service
starts refuse the option, and bridge workers cannot claim an opted-in embedded
run. Native admission and SDK runtime delivery remain pending under
[Server #1201](https://github.com/durable-workflow/server/issues/1201); parser
support alone does not implement timeout cleanup.

## Workflow-task lease {#workflow-task-lease}

A workflow task is one worker turn that replays committed history and produces durable commands. It is not the entire workflow. Waiting on a durable timer, queued activity or signal does not hold one workflow task for the whole wait. A later task resumes the run.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ Aktivitäten laufen mindestens einmal. Ein vorwärts gerichteter Aufruf kann dah

## Kompensation bei einer Anwendungsfrist {#deadline-driven-compensation}

Ein von der Engine durchgesetztes [Workflow-Zeitlimit](./timeouts.md#timeout-outcome-and-cleanup) schließt den Lauf, bevor Workflow-Code Kompensation ausführen kann. Ein `catch`- oder `finally`-Block ändert dies nicht. Auch die unmittelbaren Befehle `cancel()` und `terminate()` bieten kein Aufräumfenster.
Bei der standardmäßigen harten Timeout-Regel gilt: Ein von der Engine durchgesetztes [Workflow-Zeitlimit](./timeouts.md#timeout-outcome-and-cleanup) schließt den Lauf, bevor Workflow-Code Kompensation ausführen kann. Ein `catch`- oder `finally`-Block ändert dies nicht. Auch die unmittelbaren Befehle `cancel()` und `terminate()` bieten kein Aufräumfenster.

Die separate [experimentelle Timeout-Cleanup-Option](./timeouts.md#timeout-cleanup) kann innerhalb der ursprünglichen harten Grenze Zeit für einen durch Unterbrechung ausgelösten Kompensationshandler reservieren. Sie unterstützt Embedded-Queue-Worker; Veröffentlichung und Prüfung des installierten Pakets stehen noch aus. Sie macht Kompensation nicht automatisch und garantiert bei fehlenden Workern keine Wirkung.

Verwenden Sie eine frühere Anwendungsfrist, wenn bei Ablauf bereits erledigte Arbeit zurückgenommen werden soll. Dieser eingebettete Workflow reserviert eine Ressource und wartet danach höchstens 30 Sekunden auf ein Bestätigungssignal. Bei Ablauf wird Workflow-Code fortgesetzt, der die Reservierung vor Rückgabe des fachlichen Ergebnisses `expired` freigibt:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,57 @@ Die Fehlerzeile speichert `Workflow\V2\Exceptions\WorkflowTimeoutException` als

### Ergebnis und Aufräumen bei Zeitüberschreitung {#timeout-outcome-and-cleanup}

Eine Workflow-Frist schließt den Lauf mit Status `failed` und `closed_reason = timed_out`. Das terminale Historienereignis ist `WorkflowTimedOut`. Die Laufzeit speichert `WorkflowTimeoutException` als Fehlerdaten. Sie wirft diese Ausnahme **nicht** im Workflow-Code und setzt den Workflow nicht zum Aufräumen fort. `catch`- und `finally`-Blöcke sowie registrierte Saga-Kompensationen werden bei der Durchsetzung dieser Frist nicht ausgeführt.
Bei der standardmäßigen harten Timeout-Regel gilt: Eine Workflow-Frist schließt den Lauf mit Status `failed` und `closed_reason = timed_out`. Das terminale Historienereignis ist `WorkflowTimedOut`. Die Laufzeit speichert `WorkflowTimeoutException` als Fehlerdaten. Sie wirft diese Ausnahme **nicht** im Workflow-Code und setzt den Workflow nicht zum Aufräumen fort. `catch`- und `finally`-Blöcke sowie registrierte Saga-Kompensationen werden bei der Durchsetzung dieser Frist nicht ausgeführt.

Der Abbruch einer dauerhaften Aktivitätsausführung macht externe Nebenwirkungen nicht rückgängig und beweist nicht, dass ihr Callback beendet wurde. Verwenden Sie idempotente Vorgänge und anwendungseigene Abgleichverfahren für möglicherweise bereits übernommene Effekte.

Wenn nach einer Anwendungsfrist aufgeräumt werden muss, verwenden Sie einen früheren dauerhaften Timer oder eine zeitbegrenzte Warteoperation, die [Kompensation vor dem Abschluss](./sagas.md#deadline-driven-compensation) im Workflow-Code ermöglicht. Lassen Sie vor Lauf- und Gesamtausführungsfrist Zeit für Aktivitätswiederholungen, Worker-Zustellung und Aufräumen. Das Engine-Zeitlimit bleibt die letzte Grenze. Ein verspäteter Worker kann sie vor Abschluss der Kompensation erreichen.

Auch die unmittelbaren Befehle `cancel()` und `terminate()` schließen den Lauf ohne Aufräumfenster. Der gesonderte kooperative Vorgang `requestCancellation()` kann einem kompatiblen Worker begrenztes Aufräumen ermöglichen, solange der Lauf aktiv ist. Eine Workflow-Zeitüberschreitung wird nicht automatisch zu einer kooperativen Abbruchanfrage; ein Abbruchschutz verlängert weder Gesamt- noch Lauffrist. Siehe [Abbrechen und terminieren](./cancel-and-terminate.md) und [Kooperativer Abbruch](../polyglot/cancellation.md).

## Experimentelles deadlinegesteuertes Cleanup {#timeout-cleanup}

**Unveröffentlichte experimentelle Embedded-Unterstützung.** Diese Anleitung zielt auf Workflow 2.29.0; Veröffentlichung und Qualifikation des installierten Pakets stehen noch aus. Die ausgewählte stabile Version behält das oben beschriebene harte Timeout als Standard.

Installieren Sie vor Aktivierung die qualifizierte Version, führen Sie die Anwendungsmigrationen aus und stellen Sie kompatible Embedded-Queue-Worker bereit. Die neue Migration ergänzt `workflow_runs.timeout_cleanup_work_deadline_at`; vorhandene Runs werden dadurch nicht angemeldet. Aktivieren Sie die Option erst nach abgeschlossener Migration.

```bash
php artisan migrate
```

Reservieren Sie beim Start einen endlichen Teil des bestehenden harten Timeouts:

```php
use Workflow\V2\StartOptions;
use Workflow\V2\WorkflowStub;

WorkflowStub::make(MyWorkflow::class, 'order-123')->start(
$orderId,
StartOptions::rejectDuplicate()
->withExecutionTimeout(70)
->withTimeoutCleanup(10),
);
```

`timeoutCleanupSeconds:` ist die Konstruktoralternative. Das Budget muss1–86400 Sekunden betragen, benötigt ein Ausführungs- oder Run-Timeout und muss kleiner als jedes konfigurierte harte Timeout sein. `null` belässt die Standardregel.

Im Beispiel erhält normale Arbeit 60 Sekunden; Cleanup darf die letzten 10 Sekunden innerhalb der ursprünglichen harten 70-Sekunden-Grenze nutzen. Das Budget fügt nach dieser Grenze keine Zeit hinzu. Gelten beide harten Deadlines, begrenzt die frühere das Cleanup. Späte Erkennung, Retries, Worker-Austausch und Lease-Verlängerung verschieben keine Deadline. Continue-as-new und Redrive erhalten das aufgenommene Budget und die ursprünglichen Ausführungsuhren; jeder Run leitet seine eigenen Run-Uhren ab.

An der Arbeitsdeadline wechselt der Run einmal ins Cleanup und zeichnet `WorkflowTimeoutCleanupStarted` auf. Kompatibler Workflow-Code erhält `WorkflowTimeoutCleanupRequested`, einen Untertyp von `WorkflowCancellationRequestedException` mit Cancellation-`cause: timeout`. Behandeln Sie diese Unterbrechung für registrierte Saga-Kompensation oder geschütztes Cleanup; `compensate()` nutzt seinen Cancellation-Shield. Der Shield verlängert die harte Grenze nicht. Siehe [Sagas](./sagas.md#deadline-driven-compensation).

Cleanup hat ein separates `timeout_cleanup.outcome`:

| Ergebnis | Bedeutung |
|---|---|
| `completed` | Cleanup kehrte vor seiner Deadline zurück oder warf die Timeout-Unterbrechung erneut. Ein Rückgabewert wird nicht zum Geschäftsergebnis. |
| `failed` | Cleanup warf eine andere Ausnahme; `failure_id` verknüpft den Cleanup-Fehler. |
| `expired` | Die harte Grenze trat vor dem Cleanup-Ende ein, auch wenn kein Worker es ausführen konnte. |
| `preempted` | Sofortiges `cancel()` oder `terminate()` unterbrach Cleanup; das entsprechende Cancellation-/Terminierungsergebnis bleibt terminal. |

Abgeschlossenes, fehlgeschlagenes oder abgelaufenes Cleanup endet weiterhin mit Geschäftsstatus `failed`, `closed_reason = timed_out` und `WorkflowTimedOut`. Erfolgreiche Kompensation macht daraus keinen erfolgreichen Workflow. Prüfen Sie Describe/Run-Details und den aufbewahrten Verlauf für Geschäfts- und Cleanup-Ergebnis. Fehlende oder verzögerte Worker können keine externe Cleanup-Wirkung garantieren; halten Sie Kompensation idempotent und gleichen Sie unsichere Wirkungen in der Anwendung ab.

Dieser Schritt unterstützt nur Embedded-Queue-Worker. Poll- und Service-Starts lehnen die Option ab; Bridge-Worker können einen angemeldeten Embedded-Run nicht claimen. Native Admission und SDK-Laufzeitzustellung bleiben unter [Server #1201](https://github.com/durable-workflow/server/issues/1201) offen; Parser-Unterstützung allein implementiert kein Timeout-Cleanup.

## Lease eines Workflow-Tasks {#workflow-task-lease}

Ein Workflow-Task ist ein Verarbeitungsschritt eines Workers, der gespeicherte Historie wiedergibt und dauerhafte Befehle erzeugt. Er ist nicht der gesamte Workflow. Das Warten auf einen dauerhaften Timer, eine über die Queue ausgeführte Aktivität oder ein Signal hält keinen Workflow-Task während der ganzen Wartezeit; ein späterer Task setzt den Lauf fort.
Expand Down
4 changes: 2 additions & 2 deletions i18n/de/source-hashes.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@
"features/memo.md": "e1bb34216febf112c0d9151b8a57e31cc2de17b4bf16980812da01c0e1c17f15",
"features/message-streams.md": "8a21b2e6dff51c69a3395d50ea6167e418ed793b4b7107b964f2a0f5f03a3801",
"features/queries.md": "c66dd9542a062edd8158a46cebc9f1bf926aaf3599dca4a3122bd9860df6e2a2",
"features/sagas.md": "046ba5661ccb5ac71a236356d243b021d9830123e3798afce491118434e83820",
"features/sagas.md": "5df49e331f7802859c203120917e707cafa3141bfbf8b094430d5ad37f0131ef",
"features/schedules.md": "40d30b2767e7ed043e2b0c7180915c256accea3e7a3401c38d1bf75bfe1c2213",
"features/search-attributes.md": "bb66d83f64e0098354d86ac237e6867b9819098044101ba9a60a6a00a3961dd1",
"features/service-operations.md": "912131c0a8ef393040a933fb4a66e68abcb9d475a3ebd47c85287d4032f45a0e",
"features/side-effects.md": "abdccab8d6fc1a12b3eff8a4ccc58730ac36f523559b4ad89c5c2a8d201a3b86",
"features/signal+timer.md": "a05fc480544fa71f6c9b215ad3417f71b8475dd81f9de8ee18d7a3b69ac33a23",
"features/signals.md": "2816f53ec12f5ae88906a399a9681e8a517677d64ce6c063dd4eb205c46f370b",
"features/sticky-execution.md": "d54fe85283d633d5cba10a03c716a707bf3b6a4e6d47ec9859e17f5f676a89b8",
"features/timeouts.md": "3c9022e1f00b5aa082079895ee4966c2703c88e96cc2c98b2995b706e5440566",
"features/timeouts.md": "270c1e8f606d3caae68e852b3ea9fcb0036bbb6db670d2d74130a09774bf88be",
"features/timers.md": "8ef45889325eb3358dd7c894511dfbd647e2813fd2eb368069f17d56c90a5d27",
"features/updates.md": "e8278d18f57f0bead7b5462ecc7a3e21d879654b8321bfed1700e5616433ec02",
"features/versioning.md": "b140788b451272eff549c0064dc52433a95897ef051a145add0661632a5648bd",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ Las actividades se ejecutan al menos una vez, así que una llamada de avance pue

## Compensación por vencimiento del plazo {#deadline-driven-compensation}

Un [tiempo de espera de workflow](./timeouts.md#timeout-outcome-and-cleanup) aplicado por el motor cierra la ejecución antes de que su código pueda compensar. Un bloque `catch` o `finally` no cambia ese comportamiento. Las órdenes inmediatas `cancel()` y `terminate()` tampoco dan tiempo para la limpieza.
Con la política predeterminada de timeout estricto: Un [tiempo de espera de workflow](./timeouts.md#timeout-outcome-and-cleanup) aplicado por el motor cierra la ejecución antes de que su código pueda compensar. Un bloque `catch` o `finally` no cambia ese comportamiento. Las órdenes inmediatas `cancel()` y `terminate()` tampoco dan tiempo para la limpieza.

La [opción experimental de limpieza por timeout](./timeouts.md#timeout-cleanup) puede reservar tiempo dentro del límite estricto original para un manejador de compensación activado por una interrupción. Es soporte para workers de cola embebidos con publicación y cualificación instalada aún pendientes; no hace automática la compensación ni garantiza efectos sin workers disponibles.

Use un plazo anterior de la aplicación cuando quiera revertir el trabajo completado al agotarse el tiempo. Este workflow integrado reserva un recurso y espera hasta 30 segundos una señal de confirmación. Al vencer, el código del workflow se reanuda, libera la reserva y devuelve el resultado de negocio `expired`:

Expand Down
Loading
Loading