-
Notifications
You must be signed in to change notification settings - Fork 11
[copilot-finds] Bug: InMemoryOrchestrationBackend continue-as-new does not cancel pending timers, causing stale events in new iteration #207
Description
Problem
When an orchestration calls continueAsNew() in the InMemoryOrchestrationBackend, the instance state (history, status, input) is reset, but pending setTimeout handles from the previous iteration are not cancelled. These stale timers continue to fire and deliver TimerFired events into the new iteration.
File: packages/durabletask-js/src/testing/in-memory-backend.ts, processCompleteOrchestrationAction() (line ~475)
Root Cause
processCompleteOrchestrationAction() resets the instance state for continue-as-new but does not cancel any timers created during the previous iteration. The pendingTimers set tracks all timers globally, with no per-instance mapping, making it impossible to cancel just the timers belonging to the restarting instance.
Additionally, carryover events were placed before OrchestratorStarted and ExecutionStarted events, which is inconsistent with the real sidecar's event ordering.
Impact
Because the RuntimeOrchestrationContext resets its sequence counter on each new execution, stale timer IDs from the old iteration can collide with task IDs in the new iteration. This can cause:
- Wrong task completion — a stale
TimerFiredevent with timer ID N could match a completely different pending task (activity, sub-orchestration, or new timer) in the new iteration that happens to have the same sequence number. - Unexpected-event warnings — if no matching task exists, the executor logs a warning and silently drops the event. While not a crash, this adds noise and wastes processing.
- Test behavior divergence — the in-memory backend's behavior for continue-as-new does not match the real sidecar, which cancels old timers on instance restart.
Proposed Fix
- Add per-instance timer tracking (
instanceTimers: Map<string, Set<TimerHandle>>) - Cancel all pending timers for an instance when continue-as-new is processed
- Fix carryover event ordering so
OrchestratorStartedandExecutionStartedcome before carryover events - Add tests for timer cancellation and correct event ordering
Severity
Medium — Affects correctness of the in-memory testing backend for orchestrations that use both timers and continue-as-new. Could cause intermittent, hard-to-diagnose test failures.