Skip to content

Support status-based orchestration ID reuse policy - #352

Merged
wangbill (YunchuWang) merged 9 commits into
mainfrom
yunchuwang-support-orchestration-id-reuse
Aug 6, 2026
Merged

Support status-based orchestration ID reuse policy#352
wangbill (YunchuWang) merged 9 commits into
mainfrom
yunchuwang-support-orchestration-id-reuse

Conversation

@YunchuWang

@YunchuWang wangbill (YunchuWang) commented Aug 5, 2026

Copy link
Copy Markdown
Member

Summary

  • expose a flat StartOrchestrationOptions.dedupeStatuses API matching the merged .NET contract and serialize its complement into CreateInstanceRequest.orchestrationIdReusePolicy.replaceableStatus
  • preserve production backend defaults when the option is omitted, make [] explicitly replace every supported status, export ValidDedupeStatuses, and validate unsupported statuses
  • keep the .NET shim-only Terminated/running-status self-conflict check in the in-memory test path, where omission means all statuses reusable
  • map gRPC ALREADY_EXISTS to OrchestrationAlreadyExistsError and INVALID_ARGUMENT to TypeError, with equivalent in-memory duplicate errors
  • align in-memory replacement with terminate-before-create behavior while fencing stale activity/sub-orchestration completions and cleaning execution-owned child watchers
  • forward the flat option through azure-functions-durable and cover it against a real Azure DTS Consumption scheduler

API rationale and scope

The public shape mirrors merged durabletask-dotnet: dedupeStatuses is a top-level start option, not a nested policy object. Object literals already provide the JavaScript equivalent of the .NET WithDedupeStatuses record extension, so no builder helper is added.

Production gRPC serialization preserves three states exactly:

  • undefined: omit the protobuf policy and preserve backend duplicate-ID defaults
  • []: send all seven valid statuses as replaceable
  • non-empty: send ValidDedupeStatuses - dedupeStatuses

Like .NET GrpcDurableTaskClient, the production client validates only that each status is supported and forwards Terminated plus reusable-running combinations to the sidecar. Live DTS rejects that self-conflicting combination with INVALID_ARGUMENT, which the JS client maps to TypeError. Like .NET ShimDurableTaskClient, the in-memory client performs the same validation locally and treats omitted dedupeStatuses as all statuses reusable.

The canonical shared protocol reserves field 2 and contains only replaceableStatus; it cannot represent atomic no-op/IGNORE. This PR does not fake IGNORE with a racy client-side preflight, so that part of issue #30 remains protocol-blocked.

Compatibility

  • this PR was not merged, so the prior nested orchestrationIdReusePolicy proposal is cleanly replaced rather than retained as a compatibility alias
  • existing positional scheduleNewOrchestration(..., instanceId, startAt) calls remain supported
  • production gRPC duplicate-ID behavior is unchanged when dedupeStatuses is omitted; live DTS empirically defaults to duplicate rejection
  • the in-memory client intentionally mirrors .NET shim null behavior by replacing reusable existing instances when dedupeStatuses is omitted; this is an observable testing/production divergence inherited from the .NET client split
  • the in-memory test client rejects unsupported version and tags options instead of silently dropping them
  • CANCELED is mapped and treated as terminal across affected SDK, provider, testing, and export-history surfaces
  • the JS SDK has no named cancellation exception contract, so scheduling preserves a gRPC CANCELLED error as its raw ServiceError with status code intact
  • no generated protobuf or shared proto files were changed

Validation

  • focused reuse-policy unit suite: 22 tests passed
  • core: 69 suites, 1,238 tests passed
  • azure-functions-durable: 19 suites, 164 tests passed
  • export history: 7 suites, 66 tests passed
  • core, azure-functions-durable, and export-history builds passed
  • ESLint passed
  • targeted Prettier checks passed using the repository's CRLF working-tree convention
  • CRLF-aware git diff --check passed
  • final semantic code review reported no significant issues
  • real Azure DTS Consumption scheduler reuse-policy E2E: 1 suite, 4 tests passed in 32.683s
    • running status selected for deduplication returned the public already-exists error and preserved the original input/status
    • running status left replaceable accepted the duplicate and completed with the replacement input/output
    • a Terminated dedupe policy that left SUSPENDED reusable reached DTS, which rejected it with INVALID_ARGUMENT; the client surfaced TypeError and the original suspended instance remained untouched
    • omitted policy preserved the live scheduler's default duplicate rejection

The broader Azure-managed E2E command also connected successfully and passed orchestration.spec.ts, but exceeded the local command budget before completing all suites. The feature-specific suite was run directly and passed in full.

Refs #30

Copilot AI lite review requested due to automatic review settings August 5, 2026 17:15

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.

Pull request overview

This PR adds first-class support for orchestration instance ID reuse by introducing a public OrchestrationIdReusePolicy (status-based deduplication) and plumbing it through the core gRPC client, in-memory testing backend, and the azure-functions-durable wrapper. It also completes CANCELED status mappings end-to-end so runtime status handling stays accurate when applying reuse policies.

Changes:

  • Add OrchestrationIdReusePolicy and serialize it onto CreateInstanceRequest.orchestrationIdReusePolicy.replaceableStatus (via complement of dedupeStatuses).
  • Enable atomic “replace existing instance” behavior in the in-memory backend and forward the policy through DurableFunctionsClient.startNew().
  • Add OrchestrationStatus.CANCELED and update status conversions/tests accordingly.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
README.md Documents how to use the new orchestration ID reuse policy.
packages/durabletask-js/test/orchestration-id-reuse-policy.spec.ts Adds unit tests for request serialization and in-memory replacement semantics.
packages/durabletask-js/src/testing/test-client.ts Extends scheduleNewOrchestration overloads to accept StartOrchestrationOptions including reuse policy.
packages/durabletask-js/src/testing/in-memory-backend.ts Implements duplicate-ID policy handling and waiter/timer cleanup during replacement; adds canceled mapping.
packages/durabletask-js/src/task/options/task-options.ts Adds orchestrationIdReusePolicy to StartOrchestrationOptions.
packages/durabletask-js/src/orchestration/orchestration-id-reuse-policy.ts Introduces the public policy type and wire conversion helper.
packages/durabletask-js/src/orchestration/enum/orchestration-status.enum.ts Adds CANCELED to the public orchestration status enum.
packages/durabletask-js/src/index.ts Exports OrchestrationIdReusePolicy from the public surface.
packages/durabletask-js/src/client/client.ts Forwards the new policy into the gRPC CreateInstanceRequest.
packages/azure-functions-durable/test/unit/query-types.spec.ts Adds tests ensuring canceled vs terminated status distinctions are preserved.
packages/azure-functions-durable/test/unit/client.spec.ts Adds a test verifying startNew() forwards the reuse policy to core scheduling.
packages/azure-functions-durable/src/orchestration-status.ts Updates runtime status conversions to include CANCELED.
packages/azure-functions-durable/src/index.ts Re-exports the new core types and StartNewOptions.
packages/azure-functions-durable/src/client.ts Adds orchestrationIdReusePolicy to StartNewOptions and forwards it.
packages/azure-functions-durable/README.md Updates docs to mention orchestrationIdReusePolicy support in startNew().
packages/azure-functions-durable/CHANGELOG.md Notes forwarding of reuse policy through startNew().
CHANGELOG.md Notes new reuse policy API and the new CANCELED status member.
Suppressed comments (1)

packages/durabletask-js/src/testing/in-memory-backend.ts:592

  • isTerminalStatus does not treat ORCHESTRATION_STATUS_CANCELED as terminal. This can cause canceled instances to never be recognized as completed (e.g., waiters may hang or state transitions may behave incorrectly) even though toClientStatus now exposes CANCELED.
    return (
      status === pb.OrchestrationStatus.ORCHESTRATION_STATUS_COMPLETED ||
      status === pb.OrchestrationStatus.ORCHESTRATION_STATUS_FAILED ||
      status === pb.OrchestrationStatus.ORCHESTRATION_STATUS_TERMINATED
    );

Comment thread packages/durabletask-js/src/orchestration/orchestration-id-reuse-policy.ts Outdated
Comment thread README.md Outdated
Copilot AI review requested due to automatic review settings August 5, 2026 18:06
@YunchuWang wangbill (YunchuWang) changed the title Support orchestration ID reuse policy Support status-based orchestration ID reuse policy Aug 5, 2026

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.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

packages/durabletask-js/src/testing/test-client.ts:54

  • The overloads accept StartOrchestrationOptions, which includes tags/version, but this client throws at runtime when those are provided. Narrowing the overload type to exclude unsupported fields avoids a compile-time/runtime mismatch and guides callers to the supported surface area.
  async scheduleNewOrchestration(
    orchestrator: TOrchestrator | string,
    input?: TInput,
    options?: StartOrchestrationOptions,
  ): Promise<string>;

Comment thread packages/durabletask-js/test/orchestration-id-reuse-policy.spec.ts
Copilot AI review requested due to automatic review settings August 5, 2026 18:46

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.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated no new comments.

Suppressed comments (1)

packages/durabletask-js/src/testing/in-memory-backend.ts:113

  • createInstance decides whether an existing instance is replaceable by converting the protobuf status via toClientStatus(), which intentionally maps CONTINUED_AS_NEW to RUNNING. This can make a transient CONTINUED_AS_NEW generation replaceable whenever RUNNING is not in dedupeStatuses, contradicting the public contract/docs that CONTINUED_AS_NEW is not replaceable.

Handle ORCHESTRATION_STATUS_CONTINUED_AS_NEW explicitly as a non-replaceable status before applying the reuse policy.

    const existingInstance = this.instances.get(instanceId);
    if (existingInstance) {
      const existingStatus = this.toClientStatus(existingInstance.status);
      if (!orchestrationIdReusePolicy || orchestrationIdReusePolicy.dedupeStatuses.includes(existingStatus)) {
        throw new Error(`Orchestration instance '${instanceId}' already exists`);
      }

wangbill (YunchuWang) and others added 3 commits August 6, 2026 11:55
Expose status-based duplicate handling across the core and Azure Functions clients, including faithful in-memory test semantics.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Reject unsupported test-client scheduling metadata, document the protocol's missing atomic IGNORE action, and keep canceled status terminal across testing and export-history surfaces.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Notify a live parent when its child is replaced and cancel child-keyed watchers when their owning parent execution is replaced.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6157e6cd-9ce9-4636-a8d5-4f49f4f32de8
@YunchuWang
wangbill (YunchuWang) force-pushed the yunchuwang-support-orchestration-id-reuse branch from 71ac676 to 13f4208 Compare August 6, 2026 18:58
Expose flat dedupe statuses, validate and serialize the status complement, map duplicate errors, and align in-memory replacement semantics.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: d491ec0d-9ce9-421b-9953-7d179d53625b
Keep production gRPC validation wire-focused while applying shim-specific replacement checks and omitted-policy semantics only to the in-memory client.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: d491ec0d-9ce9-421b-9953-7d179d53625b
Use the existing connection-string contract so the reuse-policy scenarios run against either authenticated Azure DTS or the local emulator.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: d491ec0d-9ce9-421b-9953-7d179d53625b
Cover the service-side rejection of self-conflicting status policies while preserving production request forwarding and the original suspended instance.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: d491ec0d-9ce9-421b-9953-7d179d53625b
Include the orchestration ID reuse policy spec in the existing orchestration emulator group for both supported Node versions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: d491ec0d-9ce9-421b-9953-7d179d53625b
Allow exact-head emulator verification without changing the existing push, pull request, or matrix coverage.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 6157e6cd-9ce9-4636-a8d5-4f49f4f32de8
@YunchuWang
wangbill (YunchuWang) merged commit d073ca0 into main Aug 6, 2026
23 of 24 checks passed
@YunchuWang
wangbill (YunchuWang) deleted the yunchuwang-support-orchestration-id-reuse branch August 6, 2026 21:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants