Skip to content

Fix replay determinism for retry timeouts - #326

Open
andystaples wants to merge 2 commits into
mainfrom
andystaples-retry-timeout-determinism
Open

andystaples wants to merge 2 commits into
mainfrom
andystaples-retry-timeout-determinism

Conversation

@andystaples

Copy link
Copy Markdown
Contributor

Summary

Activity and sub-orchestration retry timeout decisions now use the orchestration's replay-safe logical clock instead of wall-clock time. Replaying a historical failure after the real-time deadline no longer incorrectly suppresses its retry.

RetryableTask.compute_next_delay() accepts an optional current timestamp, normalizes timezone-aware inputs to UTC, and treats naive inputs as UTC. Direct calls without a timestamp retain their wall-clock fallback. Retry attempt limits, backoff, interval caps, and the existing expiration boundary are preserved; an absent timeout remains unbounded.

Regression coverage exercises activity and sub-orchestration history replay, recorded retry timers through successful completion, deadline boundaries, timezone offsets, and calls without an explicit timestamp.

Upgrade and release coordination

Histories that recorded timeout failures based on wall-clock time can replay differently under the corrected behavior, including orchestrations that caught those failures. Drain affected instances on the previous SDK before upgrading. This change does not recover already-failed instances.

The core, Azure Managed, and Azure Functions changelogs document the fix. Provider versions and dependency minimums remain unchanged; coordinate provider releases after the corrected core SDK is released.

Fixes #316

Use orchestration time for activity and sub-orchestration retry expiration, normalize UTC timestamps, and cover replay and deadline boundaries.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 2, 2026 19:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The datetime checks mishandle timestamps whose tzinfo is non-None but utcoffset() returns None, causing host-dependent conversions that can prematurely exhaust retry timeouts.

1 open issue
What changed in this PR

This PR addresses #316 by using replay-safe orchestration time for activity and sub-orchestration retry timeouts, preventing delayed history replay from incorrectly suppressing retries.

Changes:

  • Passes logical time into retry calculations and normalizes timezone-aware timestamps while retaining the direct-call wall-clock fallback.
  • Adds regression coverage for replay, timeout boundaries, timezone handling, and successful completion after retries.
  • Documents the fix and upgrade precautions across all three packages without changing provider versions or dependency minimums.
File Description
tests/​durabletask/​test_retry_timeout.py Adds timeout and replay regression tests.
durabletask/​worker.py Supplies orchestration time to both retry paths.
durabletask/​task.py Adds explicit retry time and timestamp normalization.
durabletask-azuremanaged/​CHANGELOG.md Documents the provider impact and core upgrade guidance.
CHANGELOG.md Records the fix and warns about existing history compatibility.
azure-functions-durable/​CHANGELOG.md Documents the provider impact and core upgrade guidance.

Review effort: Balanced

Comment thread durabletask/task.py
Comment on lines +841 to +845
if current_time.tzinfo is not None:
current_time = current_time.astimezone(UTC).replace(tzinfo=None)
start_time = self._start_time
if start_time.tzinfo is not None:
start_time = start_time.astimezone(UTC).replace(tzinfo=None)
Verify expiration on the fifth failure after the 14-second deadline, with a higher attempt limit so timeout exhaustion is exercised independently.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RetryableTask.compute_next_delay uses wall-clock time causing non-deterministic failures on replay with retry_timeout

2 participants