Repository navigation
Preserve pending durable timers across signal wakeups #96
Copy link
Copy link
Closed
Labels
authority:githubGitHub is the authoritative lifecycle record for this workGitHub is the authoritative lifecycle record for this workcompletion:evidence-requiredClose only after all explicit acceptance and operational evidence is publicClose only after all explicit acceptance and operational evidence is publicintake:approvedCurrent issue title and body revision is approved for authority intakeCurrent issue title and body revision is approved for authority intakekind:defectA public product behavior is incorrectA public product behavior is incorrectpriority:P1High-priority product or release riskHigh-priority product or release riskstatus:doneDerived from the authoritative closed issue stateDerived from the authoritative closed issue state
Description
Activity
Metadata
Metadata
Assignees
Labels
authority:githubGitHub is the authoritative lifecycle record for this workGitHub is the authoritative lifecycle record for this workcompletion:evidence-requiredClose only after all explicit acceptance and operational evidence is publicClose only after all explicit acceptance and operational evidence is publicintake:approvedCurrent issue title and body revision is approved for authority intakeCurrent issue title and body revision is approved for authority intakekind:defectA public product behavior is incorrectA public product behavior is incorrectpriority:P1High-priority product or release riskHigh-priority product or release riskstatus:doneDerived from the authoritative closed issue stateDerived from the authoritative closed issue state
Observed defect
Published Python SDK 2.4.0 reissues an already scheduled durable timer when a signal wakes the workflow. The second timer receives another durable operation sequence and a later deadline. Once both timers fire, the unchanged workflow can no longer replay its following condition wait. A query reports NonDeterministicReplayError because history contains another timer where the workflow expects the condition.
This reproduces on published Server 2.5.5 before any image replacement. The resulting history also fails replay on published Server 2.5.6, with the same installed SDK and workflow code.
Reproduce
Run a normal Python worker with an echo activity and this workflow:
Start the workflow and wait for ActivityCompleted and TimerScheduled. Send increment(7) while the timer is pending. The query returns seven, but raw Server history now contains two timers for the single sleep call. The observed first timer was operation sequence 2. The second was sequence 3, with the same 120-second delay and a deadline 4.68 seconds later. Wait for the original timers to fire, then query or replay from a fresh worker.
The rejection says workflow sequence 3 recorded TimerScheduled and TimerFired, while the current workflow yielded a condition wait. No application code changed. PHP and Rust controls with the same activity/timer sequence each retain one timer.
Required outcome
Found through published cross-language image-continuity qualification. That qualification remains incomplete until this defect is corrected and the workload is rerun.