Skip to content

[copilot-finds] Bug: InMemoryOrchestrationBackend.purge() leaks pending timer handles #188

@github-actions

Description

@github-actions

Problem

InMemoryOrchestrationBackend.purge() deletes the orchestration instance from the store but does not cancel pending setTimeout handles created by processCreateTimerAction(). This causes timer handles to leak, keeping the Node.js event loop alive unnecessarily.

File: packages/durabletask-js/src/testing/in-memory-backend.ts
Lines: purge() (lines 208-221) and processCreateTimerAction() (lines 526-555)

When an orchestration creates a durable timer (e.g., ctx.createTimer(3600)), the backend schedules a setTimeout and tracks the handle in a global pendingTimers set. When the orchestration is later terminated and purged, purge() removes the instance and its state waiters but does not cancel the pending timer handles.

The timer callback does check whether the instance still exists and skips action if it does not, so this is not a correctness bug — but the timer handles remain in the Node.js event loop until they fire, which can be hours or days for long-delay timers.

Note that reset() correctly cancels all timers, but purge() does not.

Root Cause

The pendingTimers set tracks timer handles globally with no per-instance mapping. There is no way for purge() to identify which timers belong to the purged instance, so it cannot cancel them.

Proposed Fix

Add a per-instance timer tracking map (instanceTimers: Map<string, Set<ReturnType<typeof setTimeout>>>) that associates timer handles with their orchestration instance. When purge() is called, cancel all timers for that instance and remove them from both the instance-level and global tracking sets.

Impact

  • Severity: Medium — resource leak (timer handles keep event loop alive), not a correctness issue
  • Affected scenarios: Any test that creates long-delay timers and then purges the orchestration. In test suites, this can cause Jest to hang with "Jest did not exit" warnings. In long-running test processes, leaked timers accumulate memory.

Metadata

Metadata

Assignees

No one assigned

    Labels

    copilot-findsFindings from daily automated code review agent

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions