Prevent timer callbacks after orchestration completion - #280
Bernd Verst (berndverst) merged 2 commits into
Conversation
Guard timer continuation at the executor callback boundary while preserving pre-terminal actions and trailing history processing. Cover terminal states, replay, native/chunked retries, cancellation, and Functions compatibility. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The focused guard matches the requested lifecycle behavior and is supported by comprehensive regression coverage.
Review effort: Balanced
Findings: None
What changed in this PR
Prevents timer callbacks from producing work after an orchestration reaches a terminal state.
Changes:
- Adds a terminal-state guard to timer processing.
- Adds regression coverage for timers, retries, and both Functions APIs.
- Documents behavior across all affected packages.
| File | Description |
|---|---|
durabletask/worker.py |
Skips terminal timer callbacks. |
tests/durabletask/test_orchestration_executor.py |
Covers terminal timer and retry scenarios. |
tests/azure-functions-durable/test_worker_compat.py |
Tests native and compatibility APIs. |
CHANGELOG.md |
Documents the core fix. |
durabletask-azuremanaged/CHANGELOG.md |
Documents Azure Managed impact. |
azure-functions-durable/CHANGELOG.md |
Documents Functions provider impact. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Bernd Verst (berndverst)
left a comment
There was a problem hiding this comment.
Looks good. The timer-local terminal guard prevents post-terminal work while preserving legitimate earlier actions and subsequent history processing. Active timer behavior is retained, and the provider changelogs accurately distinguish native and chunked timers. No unjustified breaking change or new Python-version requirement.
Summary
Fixes #275.
Guard
timerFiredcallbacks once the orchestration context is terminal. A later timer event can no longer schedule another long-timer chunk, dispatch an activity/sub-orchestration retry, or resume suspended orchestrator code after completion, failure, termination, or continue-as-new.The guard is local to the timer callback, after task lookup and timer tracing. It does not break the history loop, change external-event handling, clear pending actions, or remove legitimate work scheduled before the terminal state. No Go coroutine-unwind/defer machinery is introduced; #274, #276, and #277 are intentionally out of scope.
Core and Azure Functions use chunked long timers. Azure Managed disables chunking because DTS supports native long timers; it benefits from the same terminal guard for retry dispatch and generator resumption, not a default chunk-continuation fix. All three Unreleased changelogs reflect this distinction. No versions or dependency minimums are changed; provider release coordination waits for the corrected core release.
Regression coverage