From 2396013f1ee0b7e336a6f50aab09e35508125915 Mon Sep 17 00:00:00 2001 From: Richard McDaniel <1130888+rmcdaniel@users.noreply.github.com> Date: Fri, 13 Nov 2026 13:01:55 +0000 Subject: [PATCH 1/2] Draft experimental embedded timeout cleanup journey --- docs/features/sagas.md | 8 ++++- docs/features/timeouts.md | 72 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/docs/features/sagas.md b/docs/features/sagas.md index 5cf39228fc5..5fe2518e22d 100644 --- a/docs/features/sagas.md +++ b/docs/features/sagas.md @@ -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 diff --git a/docs/features/timeouts.md b/docs/features/timeouts.md index 78e7d7ef159..5dba8a7d9b7 100644 --- a/docs/features/timeouts.md +++ b/docs/features/timeouts.md @@ -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. @@ -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–86,400 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. From f858c50be7dbb3fc636a7d2885ce2bd5a8ad9df8 Mon Sep 17 00:00:00 2001 From: Richard McDaniel <1130888+rmcdaniel@users.noreply.github.com> Date: Fri, 13 Nov 2026 13:52:27 +0000 Subject: [PATCH 2/2] Translate the unreleased embedded timeout cleanup journey --- docs/features/timeouts.md | 2 +- .../current/features/sagas.md | 4 +- .../current/features/timeouts.md | 45 ++++++++++++++++++- i18n/de/source-hashes.json | 4 +- .../current/features/sagas.md | 4 +- .../current/features/timeouts.md | 45 ++++++++++++++++++- i18n/es/source-hashes.json | 4 +- .../current/features/sagas.md | 4 +- .../current/features/timeouts.md | 45 ++++++++++++++++++- i18n/fr/source-hashes.json | 4 +- .../current/features/sagas.md | 4 +- .../current/features/timeouts.md | 45 ++++++++++++++++++- i18n/ja/source-hashes.json | 4 +- .../current/features/sagas.md | 4 +- .../current/features/timeouts.md | 45 ++++++++++++++++++- i18n/pt-BR/source-hashes.json | 4 +- .../current/features/sagas.md | 4 +- .../current/features/timeouts.md | 45 ++++++++++++++++++- i18n/uk/source-hashes.json | 4 +- .../current/features/sagas.md | 4 +- .../current/features/timeouts.md | 45 ++++++++++++++++++- i18n/zh-Hans/source-hashes.json | 4 +- 22 files changed, 344 insertions(+), 29 deletions(-) diff --git a/docs/features/timeouts.md b/docs/features/timeouts.md index 5dba8a7d9b7..f67abb6039d 100644 --- a/docs/features/timeouts.md +++ b/docs/features/timeouts.md @@ -181,7 +181,7 @@ WorkflowStub::make(MyWorkflow::class, 'order-123')->start( ``` `timeoutCleanupSeconds:` is the constructor alternative. The allowance must be -1–86,400 seconds, requires an execution or run timeout, and must be smaller than +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 diff --git a/i18n/de/docusaurus-plugin-content-docs/current/features/sagas.md b/i18n/de/docusaurus-plugin-content-docs/current/features/sagas.md index a3e5474b799..04b9e98473c 100644 --- a/i18n/de/docusaurus-plugin-content-docs/current/features/sagas.md +++ b/i18n/de/docusaurus-plugin-content-docs/current/features/sagas.md @@ -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: diff --git a/i18n/de/docusaurus-plugin-content-docs/current/features/timeouts.md b/i18n/de/docusaurus-plugin-content-docs/current/features/timeouts.md index 229ab77bf4c..2b34c325f03 100644 --- a/i18n/de/docusaurus-plugin-content-docs/current/features/timeouts.md +++ b/i18n/de/docusaurus-plugin-content-docs/current/features/timeouts.md @@ -127,7 +127,7 @@ 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. @@ -135,6 +135,49 @@ Wenn nach einer Anwendungsfrist aufgeräumt werden muss, verwenden Sie einen fr 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. diff --git a/i18n/de/source-hashes.json b/i18n/de/source-hashes.json index 1d7861a4083..20dfee441e1 100644 --- a/i18n/de/source-hashes.json +++ b/i18n/de/source-hashes.json @@ -43,7 +43,7 @@ "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", @@ -51,7 +51,7 @@ "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", diff --git a/i18n/es/docusaurus-plugin-content-docs/current/features/sagas.md b/i18n/es/docusaurus-plugin-content-docs/current/features/sagas.md index 644f945e279..1d57cd42f74 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/features/sagas.md +++ b/i18n/es/docusaurus-plugin-content-docs/current/features/sagas.md @@ -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`: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/features/timeouts.md b/i18n/es/docusaurus-plugin-content-docs/current/features/timeouts.md index 232a9147af2..07bac289156 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/features/timeouts.md +++ b/i18n/es/docusaurus-plugin-content-docs/current/features/timeouts.md @@ -127,7 +127,7 @@ La fila de error almacena `Workflow\V2\Exceptions\WorkflowTimeoutException` como ### Resultado del tiempo de espera y limpieza {#timeout-outcome-and-cleanup} -Un plazo de workflow cierra la ejecución con estado `failed` y `closed_reason = timed_out`. El evento terminal es `WorkflowTimedOut`. El runtime registra `WorkflowTimeoutException` como datos del error. **No** lanza esa excepción dentro del cuerpo del workflow ni reanuda su código para limpiar. Los bloques `catch` y `finally` y las compensaciones de saga registradas no se ejecutan cuando el motor aplica este plazo. +Con la política predeterminada de timeout estricto: Un plazo de workflow cierra la ejecución con estado `failed` y `closed_reason = timed_out`. El evento terminal es `WorkflowTimedOut`. El runtime registra `WorkflowTimeoutException` como datos del error. **No** lanza esa excepción dentro del cuerpo del workflow ni reanuda su código para limpiar. Los bloques `catch` y `finally` y las compensaciones de saga registradas no se ejecutan cuando el motor aplica este plazo. Cancelar la ejecución duradera de una actividad no revierte un efecto externo ni demuestra que su callback se haya detenido. Use operaciones idempotentes y conciliación propia de la aplicación para los efectos que ya podrían haberse confirmado. @@ -135,6 +135,49 @@ Si necesita limpiar tras un plazo de la aplicación, use un temporizador durader Las órdenes inmediatas `cancel()` y `terminate()` también cierran la ejecución sin dar tiempo al código de limpieza. La operación cooperativa independiente `requestCancellation()` puede entregar una limpieza acotada a un worker compatible mientras la ejecución siga activa. Un tiempo de espera de workflow no se convierte automáticamente en una solicitud de cancelación cooperativa, y un escudo de cancelación no amplía el plazo completo o individual. Consulte [Cancelar y terminar](./cancel-and-terminate.md) y [Cancelación cooperativa](../polyglot/cancellation.md). +## Limpieza experimental activada por el plazo {#timeout-cleanup} + +**Soporte embebido experimental sin publicar.** Esta guía apunta a Workflow 2.29.0; la publicación y la cualificación del paquete instalado siguen pendientes. La versión estable seleccionada conserva el timeout estricto predeterminado descrito arriba. + +Antes de activar la opción, instale la versión cualificada, ejecute las migraciones de la aplicación y despliegue workers de cola embebidos compatibles. La nueva migración añade `workflow_runs.timeout_cleanup_work_deadline_at`; no activa la opción para ejecuciones existentes. No la active antes de completar la migración. + +```bash +php artisan migrate +``` + +Reserve una parte finita del timeout estricto existente al iniciar una ejecución: + +```php +use Workflow\V2\StartOptions; +use Workflow\V2\WorkflowStub; + +WorkflowStub::make(MyWorkflow::class, 'order-123')->start( + $orderId, + StartOptions::rejectDuplicate() + ->withExecutionTimeout(70) + ->withTimeoutCleanup(10), +); +``` + +`timeoutCleanupSeconds:` es la alternativa del constructor. La reserva debe ser de 1–86400 segundos, requiere un timeout de ejecución o de run y debe ser menor que cada timeout estricto configurado. `null` conserva la política predeterminada. + +En el ejemplo, el trabajo ordinario dispone de 60 segundos y la limpieza puede usar los últimos 10 dentro del límite original de 70 segundos. La reserva nunca añade tiempo después de ese límite. Si se aplican ambos plazos estrictos, el anterior limita la limpieza. La detección tardía, los reintentos, el reemplazo de workers y la renovación del lease no reinician plazos. Continue-as-new y redrive conservan la reserva admitida y los relojes de ejecución originales; cada run deriva sus propios relojes. + +Al llegar el plazo de trabajo, la ejecución entra en limpieza una sola vez y registra `WorkflowTimeoutCleanupStarted`. El código compatible recibe `WorkflowTimeoutCleanupRequested`, subtipo de `WorkflowCancellationRequestedException` con `cause: timeout`. Maneje esa interrupción para ejecutar compensaciones de saga registradas o limpieza protegida; `compensate()` usa su escudo de cancelación. El escudo no amplía el límite estricto. Véase [Sagas](./sagas.md#deadline-driven-compensation). + +La limpieza tiene un `timeout_cleanup.outcome` independiente: + +| Resultado | Significado | +|---|---| +| `completed` | La limpieza retornó o relanzó la interrupción de timeout antes de su plazo. Un valor retornado no se convierte en resultado de negocio. | +| `failed` | La limpieza lanzó otra excepción; `failure_id` enlaza el fallo de limpieza. | +| `expired` | Llegó el límite estricto antes de terminar, incluso si ningún worker pudo ejecutar la limpieza. | +| `preempted` | `cancel()` o `terminate()` inmediato interrumpió la limpieza; el resultado de cancelación/terminación correspondiente sigue siendo terminal. | + +La limpieza completada, fallida o expirada mantiene el estado de negocio `failed`, `closed_reason = timed_out` y `WorkflowTimedOut`. Compensar correctamente no convierte el timeout en éxito. Inspeccione describe/detalle del run y el historial retenido para ambos resultados. Los workers ausentes o demorados no garantizan un efecto externo de limpieza; use compensaciones idempotentes y reconcilie los efectos inciertos en su aplicación. + +Este incremento solo admite workers de cola embebidos. Los inicios en modo poll y servicio rechazan la opción; los workers bridge no pueden reclamar una ejecución embebida con la opción. La admisión nativa y la entrega de runtime en los SDK siguen pendientes en [Server #1201](https://github.com/durable-workflow/server/issues/1201); el soporte de parser por sí solo no implementa la limpieza por timeout. + ## Lease de una tarea de workflow {#workflow-task-lease} Una tarea de workflow es un turno de un worker que reproduce el historial confirmado y genera comandos duraderos. No es todo el workflow. Esperar un temporizador duradero, una actividad en cola o una señal duradera no retiene una misma tarea durante toda la espera; una tarea posterior reanuda la ejecución. diff --git a/i18n/es/source-hashes.json b/i18n/es/source-hashes.json index 1d7861a4083..20dfee441e1 100644 --- a/i18n/es/source-hashes.json +++ b/i18n/es/source-hashes.json @@ -43,7 +43,7 @@ "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", @@ -51,7 +51,7 @@ "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", diff --git a/i18n/fr/docusaurus-plugin-content-docs/current/features/sagas.md b/i18n/fr/docusaurus-plugin-content-docs/current/features/sagas.md index 08d9ebc3db1..4f1673c48aa 100644 --- a/i18n/fr/docusaurus-plugin-content-docs/current/features/sagas.md +++ b/i18n/fr/docusaurus-plugin-content-docs/current/features/sagas.md @@ -51,7 +51,9 @@ Les activités s’exécutent au moins une fois : un appel d’avancement peut d ## Compensation déclenchée par une échéance {#deadline-driven-compensation} -Une [expiration du workflow imposée par le moteur](./timeouts.md#timeout-outcome-and-cleanup) ferme le run avant que le code puisse exécuter la compensation. Un bloc `catch` ou `finally` ne change pas ce comportement. Les commandes immédiates `cancel()` et `terminate()` n’offrent pas non plus de fenêtre de nettoyage. +Avec la politique par défaut d’expiration stricte : Une [expiration du workflow imposée par le moteur](./timeouts.md#timeout-outcome-and-cleanup) ferme le run avant que le code puisse exécuter la compensation. Un bloc `catch` ou `finally` ne change pas ce comportement. Les commandes immédiates `cancel()` et `terminate()` n’offrent pas non plus de fenêtre de nettoyage. + +L’[option expérimentale de nettoyage après timeout](./timeouts.md#timeout-cleanup) peut réserver du temps dans le plafond strict original pour un gestionnaire de compensation déclenché par interruption. Elle concerne les workers de file intégrés, avec publication et qualification installée encore en attente ; elle ne rend pas la compensation automatique et ne garantit pas d’effet sans worker disponible. Utilisez une échéance applicative plus courte si l’expiration doit annuler du travail déjà effectué. Ce workflow intégré réserve une ressource, puis attend un signal de confirmation pendant au plus 30 secondes. L’expiration reprend le code, qui libère la réservation avant de renvoyer le résultat métier `expired` : diff --git a/i18n/fr/docusaurus-plugin-content-docs/current/features/timeouts.md b/i18n/fr/docusaurus-plugin-content-docs/current/features/timeouts.md index 54a6870328a..89260353659 100644 --- a/i18n/fr/docusaurus-plugin-content-docs/current/features/timeouts.md +++ b/i18n/fr/docusaurus-plugin-content-docs/current/features/timeouts.md @@ -127,7 +127,7 @@ La ligne d’échec conserve `Workflow\V2\Exceptions\WorkflowTimeoutException` c ### État final et nettoyage après expiration {#timeout-outcome-and-cleanup} -Une échéance de workflow ferme le run avec l’état `failed` et `closed_reason = timed_out`. `WorkflowTimedOut` est l’événement terminal. Le runtime enregistre `WorkflowTimeoutException` comme donnée d’échec. Il ne lève **pas** cette exception dans le corps du workflow et ne reprend pas son code pour le nettoyage. Les blocs `catch` et `finally`, ainsi que les compensations saga enregistrées, ne sont pas exécutés quand le moteur applique cette échéance. +Avec la politique par défaut d’expiration stricte : Une échéance de workflow ferme le run avec l’état `failed` et `closed_reason = timed_out`. `WorkflowTimedOut` est l’événement terminal. Le runtime enregistre `WorkflowTimeoutException` comme donnée d’échec. Il ne lève **pas** cette exception dans le corps du workflow et ne reprend pas son code pour le nettoyage. Les blocs `catch` et `finally`, ainsi que les compensations saga enregistrées, ne sont pas exécutés quand le moteur applique cette échéance. Annuler l’exécution durable d’une activité n’annule pas un effet externe et ne prouve pas que son callback s’est arrêté. Utilisez des opérations idempotentes et une réconciliation gérée par l’application pour les effets qui ont peut-être déjà été validés. @@ -135,6 +135,49 @@ Si le nettoyage doit suivre une échéance applicative, utilisez un timer durabl Les commandes immédiates `cancel()` et `terminate()` ferment aussi le run sans fenêtre de nettoyage. L’opération coopérative distincte `requestCancellation()` peut accorder un nettoyage borné à un worker compatible tant que le run est actif. Un délai de workflow n’est pas automatiquement transformé en demande d’annulation coopérative, et un bouclier d’annulation ne prolonge aucune échéance globale ou de run. Voir [Annuler et terminer](./cancel-and-terminate.md) et [Annulation coopérative](../polyglot/cancellation.md). +## Nettoyage expérimental déclenché par une échéance {#timeout-cleanup} + +**Prise en charge intégrée expérimentale non publiée.** Ce parcours vise Workflow 2.29.0 ; publication et qualification du paquet installé restent en attente. La version stable sélectionnée conserve le timeout strict par défaut décrit ci-dessus. + +Avant d’activer l’option, installez la version qualifiée, exécutez les migrations de l’application et déployez des workers de file intégrés compatibles. La nouvelle migration ajoute `workflow_runs.timeout_cleanup_work_deadline_at` ; elle n’active pas l’option sur les runs existants. Attendez la fin de la migration avant activation. + +```bash +php artisan migrate +``` + +Réservez une partie finie du timeout strict existant au démarrage d’un 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:` est l’alternative du constructeur. La réserve doit être de 1–86400 secondes, nécessite un timeout d’exécution ou de run et doit être inférieure à chaque timeout strict configuré. `null` conserve la politique par défaut. + +Dans l’exemple, le travail ordinaire dispose de 60 secondes et le nettoyage peut utiliser les 10 dernières dans le plafond strict original de 70 secondes. La réserve n’ajoute jamais de temps après ce plafond. Si les deux échéances strictes s’appliquent, la première borne le nettoyage. Détection tardive, reprises, remplacement de worker et renouvellement de lease ne déplacent aucune échéance. Continue-as-new et redrive conservent la réserve admise et les horloges d’exécution originales ; chaque run dérive ses propres horloges. + +À l’échéance de travail, le run entre une fois en nettoyage et enregistre `WorkflowTimeoutCleanupStarted`. Le code compatible reçoit `WorkflowTimeoutCleanupRequested`, sous-type de `WorkflowCancellationRequestedException` avec `cause: timeout`. Traitez cette interruption pour exécuter les compensations de saga enregistrées ou le nettoyage protégé ; `compensate()` utilise son bouclier d’annulation. Ce bouclier ne prolonge pas le plafond strict. Voir [Sagas](./sagas.md#deadline-driven-compensation). + +Le nettoyage a un `timeout_cleanup.outcome` distinct : + +| Résultat | Signification | +|---|---| +| `completed` | Le nettoyage a renvoyé ou relancé l’interruption de timeout avant son échéance. La valeur renvoyée ne devient pas le résultat métier. | +| `failed` | Le nettoyage a levé une autre exception ; `failure_id` relie son échec. | +| `expired` | Le plafond strict est arrivé avant la fin, y compris lorsqu’aucun worker n’a pu exécuter le nettoyage. | +| `preempted` | Un `cancel()` ou `terminate()` immédiat a interrompu le nettoyage ; le résultat d’annulation/terminaison correspondant reste terminal. | + +Un nettoyage terminé, échoué ou expiré laisse le statut métier `failed`, `closed_reason = timed_out` et `WorkflowTimedOut`. Une compensation réussie ne transforme pas le timeout en succès du workflow. Consultez describe, le détail du run et l’historique retenu pour les deux résultats. Des workers absents ou retardés ne garantissent pas l’effet externe du nettoyage ; gardez les compensations idempotentes et réconciliez les effets incertains dans l’application. + +Cet incrément ne prend en charge que les workers de file intégrés. Les démarrages poll et service refusent l’option ; les workers bridge ne peuvent pas acquérir un run intégré ayant opté pour elle. L’admission native et la livraison d’exécution par les SDK restent suivies dans [Server #1201](https://github.com/durable-workflow/server/issues/1201) ; le support du parser seul n’implémente pas le nettoyage après timeout. + ## Bail d’une tâche de workflow {#workflow-task-lease} Une tâche de workflow est un tour de traitement d’un worker qui relit l’historique validé et produit des commandes durables. Ce n’est pas le workflow entier. Attendre un temporisateur durable, une activité en file d’attente ou un signal durable ne conserve pas la même tâche pendant toute l’attente. Une tâche ultérieure reprend le run. diff --git a/i18n/fr/source-hashes.json b/i18n/fr/source-hashes.json index 1d7861a4083..20dfee441e1 100644 --- a/i18n/fr/source-hashes.json +++ b/i18n/fr/source-hashes.json @@ -43,7 +43,7 @@ "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", @@ -51,7 +51,7 @@ "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", diff --git a/i18n/ja/docusaurus-plugin-content-docs/current/features/sagas.md b/i18n/ja/docusaurus-plugin-content-docs/current/features/sagas.md index e43d64dfb58..2e8026f336f 100644 --- a/i18n/ja/docusaurus-plugin-content-docs/current/features/sagas.md +++ b/i18n/ja/docusaurus-plugin-content-docs/current/features/sagas.md @@ -49,7 +49,9 @@ class BookingSagaWorkflow extends Workflow ## 期限に応じた補償処理 {#deadline-driven-compensation} -エンジンの[ワークフロータイムアウト](./timeouts.md#timeout-outcome-and-cleanup)は、ワークフローコードが補償を実行する前に実行を閉じます。`catch` や `finally` はこの動作を変えません。即時の `cancel()` と `terminate()` にもクリーンアップ猶予はありません。 +既定のハードタイムアウトの方針では、エンジンの[ワークフロータイムアウト](./timeouts.md#timeout-outcome-and-cleanup)は、ワークフローコードが補償を実行する前に実行を閉じます。`catch` や `finally` はこの動作を変えません。即時の `cancel()` と `terminate()` にもクリーンアップ猶予はありません。 + +別の[実験的なタイムアウトクリーンアップオプション](./timeouts.md#timeout-cleanup)は、元のハード上限内に、割り込みで起動する補償ハンドラーの時間を予約できます。embedded queue worker 向けで、公開とインストール済みパッケージの検証はまだです。補償を自動化したり、worker 不在時の効果を保証したりするものではありません。 タイムアウト時に完了済み作業を取り消したい場合は、アプリケーション側に早い期限を設けます。この組み込みワークフローはリソースを予約し、確認シグナルを最大 30 秒待ちます。待機が期限切れになるとコードが再開し、予約を解放してから業務結果 `expired` を返します。 diff --git a/i18n/ja/docusaurus-plugin-content-docs/current/features/timeouts.md b/i18n/ja/docusaurus-plugin-content-docs/current/features/timeouts.md index e7be5f97ae5..c49cafa4b68 100644 --- a/i18n/ja/docusaurus-plugin-content-docs/current/features/timeouts.md +++ b/i18n/ja/docusaurus-plugin-content-docs/current/features/timeouts.md @@ -125,7 +125,7 @@ $description['run']['run_deadline_at']; // ISO 8601 timestamp ### タイムアウトの結果とクリーンアップ {#timeout-outcome-and-cleanup} -ワークフローの期限に達すると、実行は状態 `failed`、`closed_reason = timed_out` で閉じます。終端の履歴イベントは `WorkflowTimedOut` です。runtime は `WorkflowTimeoutException` を失敗データとして記録します。この例外をワークフロー本体へ投げたり、クリーンアップのためにコードを再開したりは**しません**。エンジンが期限を適用したときは、ワークフローの `catch`、`finally`、登録済み Saga 補償処理は実行されません。 +既定のハードタイムアウトの方針では、ワークフローの期限に達すると、実行は状態 `failed`、`closed_reason = timed_out` で閉じます。終端の履歴イベントは `WorkflowTimedOut` です。runtime は `WorkflowTimeoutException` を失敗データとして記録します。この例外をワークフロー本体へ投げたり、クリーンアップのためにコードを再開したりは**しません**。エンジンが期限を適用したときは、ワークフローの `catch`、`finally`、登録済み Saga 補償処理は実行されません。 アクティビティの永続実行をキャンセルしても、外部での副作用が取り消されるわけではなく、コールバックが停止した証明にもなりません。すでに確定している可能性のある処理には、冪等な操作とアプリケーション側の照合・修復を使用してください。 @@ -133,6 +133,49 @@ $description['run']['run_deadline_at']; // ISO 8601 timestamp 即時の `cancel()` と `terminate()` も、ワークフローのクリーンアップ猶予なしで実行を閉じます。別の操作である協調的 `requestCancellation()` は、実行が有効な間に、対応 Worker へ上限付きのクリーンアップ機会を渡せます。ワークフローのタイムアウトが自動で協調的キャンセル要求になることはなく、キャンセルシールドも実行全体または単一実行の期限を延長しません。[キャンセルと強制終了](./cancel-and-terminate.md)、[協調的キャンセル](../polyglot/cancellation.md)を参照してください。 +## 期限による実験的なタイムアウトクリーンアップ {#timeout-cleanup} + +**未公開の実験的 embedded サポート。** この手順は Workflow 2.29.0 向けで、公開とインストール済みパッケージの検証はまだです。選択された安定版では、上記の既定のハードタイムアウトを維持します。 + +有効化する前に、検証済みリリースをインストールし、アプリケーションの migration を実行し、互換性のある embedded queue worker を配置してください。新しい migration は `workflow_runs.timeout_cleanup_work_deadline_at` を追加しますが、既存の run でオプションを有効にはしません。migration の完了前に有効化しないでください。 + +```bash +php artisan migrate +``` + +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:` も使えます。予約は 1–86400 秒で、execution または run timeout が必要であり、設定されたすべてのハードタイムアウトより短くなければなりません。`null` は既定の方針を維持します。 + +この例では通常の作業に 60 秒、クリーンアップに元の 70 秒のハード上限内の最後の 10 秒を使えます。予約は上限後の時間を追加しません。両方のハード期限が適用される場合は、早い方がクリーンアップを制限します。検出の遅れ、再試行、worker 交代、lease 更新で期限は延びません。continue-as-new と redrive は受け付け済みの予約と元の execution の時計を引き継ぎ、各 run は自分の時計を導出します。 + +作業期限になると run は一度だけクリーンアップに入り、`WorkflowTimeoutCleanupStarted` を記録します。互換性のあるコードは、`cause: timeout` を持つ `WorkflowCancellationRequestedException` のサブタイプ `WorkflowTimeoutCleanupRequested` を受け取ります。この割り込みを処理して、登録した saga 補償や保護されたクリーンアップを実行してください。`compensate()` は cancellation shield を使いますが、shield はハード上限を延長しません。[Sagas](./sagas.md#deadline-driven-compensation)を参照してください。 + +クリーンアップには独立した `timeout_cleanup.outcome` があります。 + +| 結果 | 意味 | +|---|---| +| `completed` | クリーンアップが期限前に戻ったか、タイムアウト割り込みを再スローしました。戻り値は業務結果になりません。 | +| `failed` | クリーンアップが別の例外を送出し、`failure_id` がクリーンアップの失敗を参照します。 | +| `expired` | クリーンアップの完了前にハード上限に達しました。実行できる worker がいなかった場合も含みます。 | +| `preempted` | 即時の `cancel()` または `terminate()` がクリーンアップを中断し、対応するキャンセル/終了の結果が終端となります。 | + +クリーンアップの完了、失敗、期限切れのいずれでも、業務状態は `failed`、`closed_reason = timed_out`、`WorkflowTimedOut` です。補償が成功しても、タイムアウトが workflow の成功には変わりません。describe、run detail、保持された履歴で、業務結果とクリーンアップ結果の両方を確認してください。不在または遅延した worker は外部クリーンアップの効果を保証できません。補償を冪等にし、効果が不明な場合はアプリケーションで照合してください。 + +この実装は embedded queue worker のみを対象とします。poll mode と service の開始はオプションを拒否し、bridge worker は有効化された embedded run を claim できません。native admission と SDK の runtime delivery は [Server #1201](https://github.com/durable-workflow/server/issues/1201)で対応待ちです。parser の対応だけではタイムアウトクリーンアップは実装されません。 + ## ワークフロータスクのリース {#workflow-task-lease} ワークフロータスクは、確定済み履歴をリプレイして永続的なコマンドを生成する、Workerの1回の処理です。ワークフロー全体ではありません。永続的なタイマー、キュー経由のアクティビティ、シグナルを待つ間、同じタスクを保持し続けることはありません。後のタスクがrunを再開します。 diff --git a/i18n/ja/source-hashes.json b/i18n/ja/source-hashes.json index 1d7861a4083..20dfee441e1 100644 --- a/i18n/ja/source-hashes.json +++ b/i18n/ja/source-hashes.json @@ -43,7 +43,7 @@ "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", @@ -51,7 +51,7 @@ "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", diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/features/sagas.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/features/sagas.md index 4faa2bf69c5..8441b95259a 100644 --- a/i18n/pt-BR/docusaurus-plugin-content-docs/current/features/sagas.md +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/features/sagas.md @@ -51,7 +51,9 @@ Atividades são executadas pelo menos uma vez, então uma chamada de avanço pod ## Compensação por vencimento do prazo {#deadline-driven-compensation} -Um [tempo limite de workflow](./timeouts.md#timeout-outcome-and-cleanup) aplicado pelo motor encerra a execução antes que seu código possa compensar. Um bloco `catch` ou `finally` não muda esse comportamento. Os comandos imediatos `cancel()` e `terminate()` também não dão tempo para a limpeza. +Na política padrão de timeout rígido: Um [tempo limite de workflow](./timeouts.md#timeout-outcome-and-cleanup) aplicado pelo motor encerra a execução antes que seu código possa compensar. Um bloco `catch` ou `finally` não muda esse comportamento. Os comandos imediatos `cancel()` e `terminate()` também não dão tempo para a limpeza. + +A [opção experimental de limpeza por timeout](./timeouts.md#timeout-cleanup) pode reservar tempo dentro do limite rígido original para um handler de compensação acionado por interrupção. É suporte para workers de fila embutidos, com publicação e qualificação instalada ainda pendentes; não torna a compensação automática nem garante efeitos sem workers disponíveis. Use um prazo anterior da aplicação quando quiser desfazer o trabalho concluído ao esgotar o tempo. Este workflow incorporado reserva um recurso e espera até 30 segundos por um sinal de confirmação. Ao vencer, o código do workflow retoma, libera a reserva e retorna o resultado de negócio `expired`: diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/features/timeouts.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/features/timeouts.md index bfc8e439ea1..37a6ce6aeea 100644 --- a/i18n/pt-BR/docusaurus-plugin-content-docs/current/features/timeouts.md +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/features/timeouts.md @@ -127,7 +127,7 @@ A linha de falha armazena `Workflow\V2\Exceptions\WorkflowTimeoutException` como ### Resultado do tempo limite e limpeza {#timeout-outcome-and-cleanup} -Um prazo de workflow encerra a execução com estado `failed` e `closed_reason = timed_out`. O evento terminal é `WorkflowTimedOut`. O runtime registra `WorkflowTimeoutException` como dados da falha. Ele **não** lança essa exceção dentro do corpo do workflow nem retoma seu código para limpeza. Os blocos `catch` e `finally` e as compensações de saga registradas não executam quando o motor aplica esse prazo. +Na política padrão de timeout rígido: Um prazo de workflow encerra a execução com estado `failed` e `closed_reason = timed_out`. O evento terminal é `WorkflowTimedOut`. O runtime registra `WorkflowTimeoutException` como dados da falha. Ele **não** lança essa exceção dentro do corpo do workflow nem retoma seu código para limpeza. Os blocos `catch` e `finally` e as compensações de saga registradas não executam quando o motor aplica esse prazo. Cancelar a execução durável de uma atividade não desfaz um efeito externo nem prova que seu callback parou. Use operações idempotentes e reconciliação da própria aplicação para os efeitos que já podem ter sido confirmados. @@ -135,6 +135,49 @@ Se for preciso limpar após um prazo da aplicação, use um temporizador duráve Os comandos imediatos `cancel()` e `terminate()` também encerram a execução sem dar tempo ao código de limpeza. A operação cooperativa independente `requestCancellation()` pode entregar uma limpeza limitada a um worker compatível enquanto a execução estiver ativa. Um tempo limite de workflow não se transforma automaticamente em solicitação de cancelamento cooperativo, e uma proteção contra cancelamento não amplia o prazo completo ou individual. Consulte [Cancelar e terminar](./cancel-and-terminate.md) e [Cancelamento cooperativo](../polyglot/cancellation.md). +## Limpeza experimental acionada pelo prazo {#timeout-cleanup} + +**Suporte embutido experimental ainda não publicado.** Esta jornada visa Workflow 2.29.0; a publicação e a qualificação do pacote instalado continuam pendentes. A versão estável selecionada mantém o timeout rígido padrão descrito acima. + +Antes de ativar a opção, instale a versão qualificada, execute as migrações da aplicação e implante workers de fila embutidos compatíveis. A nova migração adiciona `workflow_runs.timeout_cleanup_work_deadline_at`; não ativa a opção em runs existentes. Não habilite antes de a migração terminar. + +```bash +php artisan migrate +``` + +Reserve uma parte finita do timeout rígido existente ao iniciar um 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:` é a alternativa do construtor. A reserva deve ser de 1–86400 segundos, exige um timeout de execução ou de run e deve ser menor que cada timeout rígido configurado. `null` mantém a política padrão. + +No exemplo, o trabalho comum recebe 60 segundos e a limpeza pode usar os últimos 10 dentro do limite rígido original de 70 segundos. A reserva nunca adiciona tempo depois desse limite. Se ambos os prazos rígidos se aplicam, o anterior limita a limpeza. Detecção tardia, retries, substituição de worker e renovação de lease não reiniciam prazos. Continue-as-new e redrive preservam a reserva admitida e os relógios de execução originais; cada run deriva os próprios relógios. + +No prazo de trabalho, o run entra em limpeza uma vez e registra `WorkflowTimeoutCleanupStarted`. O código compatível recebe `WorkflowTimeoutCleanupRequested`, subtipo de `WorkflowCancellationRequestedException` com `cause: timeout`. Trate essa interrupção para executar compensações de saga registradas ou limpeza protegida; `compensate()` usa seu escudo de cancelamento. O escudo não estende o limite rígido. Veja [Sagas](./sagas.md#deadline-driven-compensation). + +A limpeza tem um `timeout_cleanup.outcome` separado: + +| Resultado | Significado | +|---|---| +| `completed` | A limpeza retornou ou relançou a interrupção de timeout antes do prazo. Um valor retornado não se torna o resultado de negócio. | +| `failed` | A limpeza lançou outra exceção; `failure_id` vincula a falha da limpeza. | +| `expired` | O limite rígido chegou antes de a limpeza terminar, inclusive quando nenhum worker pôde executá-la. | +| `preempted` | `cancel()` ou `terminate()` imediato interrompeu a limpeza; o resultado de cancelamento/terminação correspondente permanece terminal. | + +Limpeza concluída, falha ou expirada ainda termina com status de negócio `failed`, `closed_reason = timed_out` e `WorkflowTimedOut`. Compensar com sucesso não transforma o timeout em workflow bem-sucedido. Inspecione describe/detalhe do run e o histórico retido para ambos os resultados. Workers indisponíveis ou atrasados não garantem um efeito externo de limpeza; mantenha compensações idempotentes e reconcilie efeitos incertos na aplicação. + +Este incremento só suporta workers de fila embutidos. Inícios poll e de serviço recusam a opção; workers bridge não podem reclamar um run embutido com ela ativada. Admissão nativa e entrega de runtime pelos SDKs seguem pendentes em [Server #1201](https://github.com/durable-workflow/server/issues/1201); suporte ao parser sozinho não implementa limpeza por timeout. + ## Lease de uma tarefa de workflow {#workflow-task-lease} Uma tarefa de workflow é uma rodada de um worker que reproduz o histórico confirmado e produz comandos duráveis. Não é o workflow inteiro. Esperar um temporizador durável, uma atividade em fila ou um sinal durável não mantém uma única tarefa durante toda a espera; uma tarefa posterior retoma a execução. diff --git a/i18n/pt-BR/source-hashes.json b/i18n/pt-BR/source-hashes.json index 1d7861a4083..20dfee441e1 100644 --- a/i18n/pt-BR/source-hashes.json +++ b/i18n/pt-BR/source-hashes.json @@ -43,7 +43,7 @@ "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", @@ -51,7 +51,7 @@ "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", diff --git a/i18n/uk/docusaurus-plugin-content-docs/current/features/sagas.md b/i18n/uk/docusaurus-plugin-content-docs/current/features/sagas.md index eee8594dbdf..2206055fab7 100644 --- a/i18n/uk/docusaurus-plugin-content-docs/current/features/sagas.md +++ b/i18n/uk/docusaurus-plugin-content-docs/current/features/sagas.md @@ -53,11 +53,13 @@ Activity виконуються щонайменше один раз, тому ## Компенсація за кінцевим терміном {#deadline-driven-compensation} -[Тайм-аут workflow](./timeouts.md#timeout-outcome-and-cleanup), який застосовує +За типової політики жорсткого тайм-ауту: [Тайм-аут workflow](./timeouts.md#timeout-outcome-and-cleanup), який застосовує рушій, закриває запуск до виконання компенсації кодом workflow. Блок `catch` або `finally` цього не змінює. Негайні команди `cancel()` і `terminate()` також не надають вікна для очищення. +Окрема [експериментальна опція очищення за тайм-аутом](./timeouts.md#timeout-cleanup) може зарезервувати час усередині початкової жорсткої межі для обробника компенсації, викликаного перериванням. Це підтримка вбудованих воркерів черги, публікація та кваліфікація встановленого пакета ще очікуються; компенсація не стає автоматичною і без доступних воркерів ефект не гарантується. + Якщо сплив часу має скасувати вже виконану роботу, задайте раніший прикладний термін. Цей вбудований workflow резервує ресурс і до 30 секунд чекає на signal підтвердження. Після спливу терміну код workflow відновлюється, звільняє ресурс diff --git a/i18n/uk/docusaurus-plugin-content-docs/current/features/timeouts.md b/i18n/uk/docusaurus-plugin-content-docs/current/features/timeouts.md index 53de4914365..6fea1490118 100644 --- a/i18n/uk/docusaurus-plugin-content-docs/current/features/timeouts.md +++ b/i18n/uk/docusaurus-plugin-content-docs/current/features/timeouts.md @@ -125,7 +125,7 @@ $description['run']['run_deadline_at']; // ISO 8601 timestamp ### Результат тайм-ауту й очищення {#timeout-outcome-and-cleanup} -Кінцевий термін workflow закриває запуск зі станом `failed` та +За типової політики жорсткого тайм-ауту: Кінцевий термін workflow закриває запуск зі станом `failed` та `closed_reason = timed_out`. Термінальна подія історії — `WorkflowTimedOut`. Рушій записує `WorkflowTimeoutException` у дані помилки. Він **не** передає цей виняток у тіло workflow й не відновлює його код для очищення. Блоки `catch` і @@ -152,6 +152,49 @@ shield не подовжує кінцевий термін виконання ч [Скасування та припинення](./cancel-and-terminate.md) і [Cooperative Cancellation](../polyglot/cancellation.md). +## Експериментальне очищення за кінцевим терміном {#timeout-cleanup} + +**Неопублікована експериментальна вбудована підтримка.** Цей сценарій призначений для Workflow 2.29.0; публікація та кваліфікація встановленого пакета ще очікуються. Обрана стабільна версія зберігає описаний вище типовий жорсткий тайм-аут. + +Перед увімкненням установіть кваліфікований реліз, виконайте міграції застосунку та розгорніть сумісні вбудовані воркери черги. Нова міграція додає `workflow_runs.timeout_cleanup_work_deadline_at`; вона не вмикає опцію для наявних запусків. Не вмикайте її до завершення міграції. + +```bash +php artisan migrate +``` + +Під час старту зарезервуйте скінченну частину наявного жорсткого тайм-ауту: + +```php +use Workflow\V2\StartOptions; +use Workflow\V2\WorkflowStub; + +WorkflowStub::make(MyWorkflow::class, 'order-123')->start( + $orderId, + StartOptions::rejectDuplicate() + ->withExecutionTimeout(70) + ->withTimeoutCleanup(10), +); +``` + +`timeoutCleanupSeconds:` — альтернатива конструктора. Резерв має бути 1–86400 секунд, потребує тайм-ауту виконання або запуску й мусить бути меншим за кожний налаштований жорсткий тайм-аут. `null` зберігає типову політику. + +У прикладі звичайна робота має 60 секунд, а очищення може використати останні 10 усередині початкової жорсткої межі 70 секунд. Резерв ніколи не додає часу після цієї межі. Якщо діють обидва жорсткі терміни, очищення обмежує раніший. Пізнє виявлення, повтори, заміна воркера та поновлення lease не пересувають терміни. Continue-as-new і redrive зберігають прийнятий резерв та початкові годинники виконання; кожний запуск виводить власні годинники. + +За робочого терміну запуск один раз переходить до очищення та записує `WorkflowTimeoutCleanupStarted`. Сумісний код отримує `WorkflowTimeoutCleanupRequested`, підтип `WorkflowCancellationRequestedException` із `cause: timeout`. Обробіть переривання для зареєстрованих компенсацій saga чи захищеного очищення; `compensate()` використовує свій щит скасування. Щит не подовжує жорстку межу. Дивіться [Sagas](./sagas.md#deadline-driven-compensation). + +Очищення має окремий `timeout_cleanup.outcome`: + +| Результат | Значення | +|---|---| +| `completed` | Очищення повернулося або повторно кинуло переривання тайм-ауту до свого терміну. Повернене значення не стає бізнес-результатом. | +| `failed` | Очищення кинуло інший виняток; `failure_id` посилається на збій очищення. | +| `expired` | Жорстка межа настала до завершення, зокрема якщо жоден воркер не міг виконати очищення. | +| `preempted` | Негайний `cancel()` або `terminate()` перервав очищення; відповідний результат скасування/припинення залишається термінальним. | + +Завершене, невдале чи прострочене очищення все одно має бізнес-статус `failed`, `closed_reason = timed_out` і `WorkflowTimedOut`. Успішна компенсація не перетворює тайм-аут на успішний workflow. Переглядайте describe/деталі запуску та збережену історію для обох результатів. Недоступні чи затримані воркери не гарантують зовнішнього ефекту очищення; робіть компенсації ідемпотентними та звіряйте невизначені ефекти в застосунку. + +Цей інкремент підтримує лише вбудовані воркери черги. Старти poll і service відхиляють опцію; bridge-воркери не можуть отримати вбудований запуск, що її увімкнув. Нативна admission і доставка виконання SDK залишаються в [Server #1201](https://github.com/durable-workflow/server/issues/1201); сам parser не реалізує очищення за тайм-аутом. + ## Lease для workflow task {#workflow-task-lease} Workflow task — це один цикл роботи worker, який відтворює зафіксовану історію та створює довговічні команди. Це не весь workflow. Очікування довговічного timer, activity у черзі або signal не утримує одну workflow task протягом усього очікування. Пізніша task відновлює запуск. diff --git a/i18n/uk/source-hashes.json b/i18n/uk/source-hashes.json index 1d7861a4083..20dfee441e1 100644 --- a/i18n/uk/source-hashes.json +++ b/i18n/uk/source-hashes.json @@ -43,7 +43,7 @@ "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", @@ -51,7 +51,7 @@ "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", diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/features/sagas.md b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/features/sagas.md index 5425a0a7eb2..cc751181178 100644 --- a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/features/sagas.md +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/features/sagas.md @@ -49,7 +49,9 @@ class BookingSagaWorkflow extends Workflow ## 由截止时间驱动的补偿 {#deadline-driven-compensation} -引擎[工作流超时](./timeouts.md#timeout-outcome-and-cleanup)会在工作流代码运行补偿前关闭运行。`catch` 或 `finally` 不会改变该行为。立即执行的 `cancel()` 与 `terminate()` 也不提供清理窗口。 +默认硬超时策略下,引擎[工作流超时](./timeouts.md#timeout-outcome-and-cleanup)会在工作流代码运行补偿前关闭运行。`catch` 或 `finally` 不会改变该行为。立即执行的 `cancel()` 与 `terminate()` 也不提供清理窗口。 + +独立的[实验性超时清理选项](./timeouts.md#timeout-cleanup)可在原始硬上限内为中断驱动的补偿处理器预留时间。它仅支持嵌入式队列 worker,发布与安装后验证仍未完成;不会使补偿自动发生,也不会在 worker 不可用时保证外部效果。 如果超时应撤销已完成工作,请使用更早的应用截止时间。此嵌入式工作流先预订资源,再最多等待确认信号 30 秒。等待到期后,工作流代码恢复,释放预订,然后返回 `expired` 业务结果: diff --git a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/features/timeouts.md b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/features/timeouts.md index 855d0e27b0b..ea424d64286 100644 --- a/i18n/zh-Hans/docusaurus-plugin-content-docs/current/features/timeouts.md +++ b/i18n/zh-Hans/docusaurus-plugin-content-docs/current/features/timeouts.md @@ -125,7 +125,7 @@ $description['run']['run_deadline_at']; // ISO 8601 timestamp ### 超时结果与清理 {#timeout-outcome-and-cleanup} -工作流到达截止时间后,以 `failed` 状态和 `closed_reason = timed_out` 关闭。终态历史事件为 `WorkflowTimedOut`。运行时将 `WorkflowTimeoutException` 记录为失败数据,**不会**将异常抛入工作流主体,也不会为清理而恢复工作流代码。因此,引擎强制执行截止时间时,工作流的 `catch`、`finally` 和已注册的 saga 补偿都不会运行。 +默认硬超时策略下,工作流到达截止时间后,以 `failed` 状态和 `closed_reason = timed_out` 关闭。终态历史事件为 `WorkflowTimedOut`。运行时将 `WorkflowTimeoutException` 记录为失败数据,**不会**将异常抛入工作流主体,也不会为清理而恢复工作流代码。因此,引擎强制执行截止时间时,工作流的 `catch`、`finally` 和已注册的 saga 补偿都不会运行。 取消活动的持久化执行并不会撤销外部副作用,也不能证明回调已停止。对于可能已提交的效果,应使用幂等操作与应用负责的核对恢复。 @@ -133,6 +133,49 @@ $description['run']['run_deadline_at']; // ISO 8601 timestamp 立即执行的 `cancel()` 和 `terminate()` 也会关闭运行,不提供工作流清理窗口。独立的协作式 `requestCancellation()` 可在运行仍活动时,由兼容 Worker 执行有界清理。工作流超时不会自动转为协作式取消请求,取消屏蔽也不会延长执行或运行截止时间。参阅[取消与终止](./cancel-and-terminate.md)及[协作式取消](../polyglot/cancellation.md)。 +## 截止时间触发的实验性清理 {#timeout-cleanup} + +**尚未发布的实验性嵌入式支持。** 此流程面向 Workflow 2.29.0;发布和安装后验证仍在等待中。当前选定的稳定版继续使用上述默认硬超时行为。 + +启用前,先安装经过验证的版本,运行应用 migration,并部署兼容的嵌入式队列 worker。新 migration 添加 `workflow_runs.timeout_cleanup_work_deadline_at`,不会为已有运行启用选项。migration 完成前不要启用。 + +```bash +php artisan migrate +``` + +启动运行时,从现有硬超时中预留有限的一部分: + +```php +use Workflow\V2\StartOptions; +use Workflow\V2\WorkflowStub; + +WorkflowStub::make(MyWorkflow::class, 'order-123')->start( + $orderId, + StartOptions::rejectDuplicate() + ->withExecutionTimeout(70) + ->withTimeoutCleanup(10), +); +``` + +构造函数也可使用 `timeoutCleanupSeconds:`。预留范围为 1–86400 秒,必须设置 execution 或 run timeout,且必须小于每一个配置的硬超时。`null` 保持默认策略。 + +本例为普通工作提供 60 秒,清理可使用原始 70 秒硬上限内的最后 10 秒。预留绝不在硬上限后增加时间。若两个硬截止时间都适用,较早的一个限制清理。延迟检测、重试、worker 替换和 lease 续期都不重置任何截止时间。continue-as-new 和 redrive 保留已接纳的预留及原始 execution 时钟;每个 run 推导自己的时钟。 + +达到工作截止时间后,运行只进入一次清理并记录 `WorkflowTimeoutCleanupStarted`。兼容代码收到 `WorkflowTimeoutCleanupRequested`,它是 `WorkflowCancellationRequestedException` 的子类型,取消上下文包含 `cause: timeout`。处理此中断以执行已注册的 saga 补偿或受保护清理;`compensate()` 使用其取消屏蔽,但屏蔽不延长硬上限。参阅 [Sagas](./sagas.md#deadline-driven-compensation)。 + +清理具有独立的 `timeout_cleanup.outcome`: + +| 结果 | 含义 | +|---|---| +| `completed` | 清理在截止时间前返回或重新抛出超时中断。返回值不会成为业务结果。 | +| `failed` | 清理抛出其他异常;`failure_id` 链接清理失败。 | +| `expired` | 硬上限先于清理完成到达,包括没有 worker 能运行清理的情况。 | +| `preempted` | 立即 `cancel()` 或 `terminate()` 中断清理;对应的取消/终止结果仍为终态。 | + +清理完成、失败或过期后,业务状态仍是 `failed`、`closed_reason = timed_out` 和 `WorkflowTimedOut`。补偿成功不会把超时变为 workflow 成功。应查看 describe/run detail 和保留历史中的业务与清理结果。不可用或延迟的 worker 无法保证外部清理效果;补偿必须幂等,并由应用核对不确定的效果。 + +本增量仅支持嵌入式队列 worker。poll 和 service 启动会拒绝此选项;bridge worker 无法 claim 已启用的嵌入式运行。原生接纳及 SDK runtime 交付仍由 [Server #1201](https://github.com/durable-workflow/server/issues/1201) 跟踪;仅有 parser 支持不代表实现超时清理。 + ## 工作流任务租约 {#workflow-task-lease} 工作流任务是Worker的一轮处理:重放已提交的历史,并生成持久化命令。它不是整个工作流。等待持久定时器、排队活动或信号时,不会在整个等待期间一直持有同一个工作流任务;后续任务会恢复运行。 diff --git a/i18n/zh-Hans/source-hashes.json b/i18n/zh-Hans/source-hashes.json index 1d7861a4083..20dfee441e1 100644 --- a/i18n/zh-Hans/source-hashes.json +++ b/i18n/zh-Hans/source-hashes.json @@ -43,7 +43,7 @@ "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", @@ -51,7 +51,7 @@ "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",