Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,50 @@ For runnable DTS emulator examples that demonstrate versioning, see the [WorkerV

The [on-demand sandbox activities sample](samples/on-demand-sandbox/README.md) shows how to declare selected activities for Durable Task Scheduler (DTS)-managed on-demand sandbox execution and build the remote worker container image separately from the declarer app.

### Prevent duplicate orchestration starts

Set `StartOrchestrationOptions.InstanceId` to a stable ID and use `DedupeStatuses` to control when an existing instance prevents another start with that ID. Deduplication is based on the instance ID and the existing instance's runtime status, not its input or orchestrator name.

| `DedupeStatuses` value | Scheduling behavior |
| --- | --- |
| `null` (default) | Uses the backend or client implementation's default deduplication behavior. |
| A nonempty list | Rejects the start with `OrchestrationAlreadyExistsException` if an instance with the same ID has a listed status. Statuses omitted from the list allow ID reuse, subject to backend support and configuration. |
| An empty list | Requests reuse for all statuses, including active instances. This does **not** prevent duplicate starts. |

If no instance with that ID exists, the request can create a new orchestration regardless of the configured statuses. Without an explicit instance ID, the client generates a new ID, so this setting does not deduplicate repeated calls.

For a singleton that must not replace an active instance but can run again after completion, failure, or termination, use the type-safe `WithDedupeStatuses` extension:

```csharp
using DurableTask.Core.Exceptions;
using Microsoft.DurableTask;
using Microsoft.DurableTask.Client;

StartOrchestrationOptions options = new StartOrchestrationOptions("singleton-workflow")
.WithDedupeStatuses(
OrchestrationRuntimeStatus.Pending,
OrchestrationRuntimeStatus.Running,
OrchestrationRuntimeStatus.Suspended);

try
{
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
"SingletonWorkflow", options);
}
catch (OrchestrationAlreadyExistsException)
{
// An active instance already owns this ID; leave it running.
}
```

`client` is a configured `DurableTaskClient`, and `"SingletonWorkflow"` is the name of a registered orchestrator. Perform this scheduling from client code, not from inside an orchestrator.

The supported status names are `Pending`, `Running`, `Suspended`, `Completed`, `Failed`, `Terminated`, and `Canceled` (retained for compatibility). You can also assign these case-insensitive strings directly to `DedupeStatuses`. To prevent reuse for every supported status, pass `StartOrchestrationOptionsExtensions.ValidDedupeStatuses.ToArray()` to `WithDedupeStatuses` (requires `using System.Linq;`). Deduplication only applies while the existing instance is retained; purging it removes that protection.

**Replacement caution:** omitting `Pending`, `Running`, or `Suspended` can allow an active orchestration to be terminated before its replacement starts. Do not include `Terminated` while allowing any of those active statuses to be reused. Clients that terminate before replacement reject that combination with `ArgumentException`, because the terminated instance would immediately block the replacement.

Backend and host support determine whether an explicit reuse policy is honored. In particular, Durable Functions behavior also depends on the Functions extension and storage provider versions; do not assume every deployment supports the same policy.

### Blob auto-purge infrastructure integration

The optional service contract is maintained in
Expand Down
29 changes: 27 additions & 2 deletions src/Abstractions/TaskOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,35 @@ public StartOrchestrationOptions(StartOrchestrationOptions options)
public TaskVersion? Version { get; init; }

/// <summary>
/// Gets the orchestration runtime statuses that should be considered for deduplication.
/// Gets the runtime statuses that prevent a new orchestration from being scheduled with an existing instance ID.
/// </summary>
/// <remarks>
/// For type-safe usage, use the WithDedupeStatuses extension method.
/// <para>
/// When an orchestration with the same <see cref="InstanceId"/> exists and its runtime status is in this list,
/// the scheduling request fails with an <c>OrchestrationAlreadyExistsException</c>. If no such instance exists,
/// the request can create a new orchestration.
/// </para>
/// <para>
/// Statuses omitted from this list allow reuse of the instance ID, subject to backend support and configuration.
/// Reusing an instance ID in a non-terminal state (Pending, Running, or Suspended) can terminate the existing
/// orchestration before creating its replacement. Include these statuses to prevent replacement of active instances.
/// </para>
/// <para>
/// The default value is <see langword="null"/>, which uses the default deduplication behavior of the backend
/// or client implementation. An empty list requests reuse for all statuses; it does not prevent duplicate starts.
/// </para>
/// <para>
/// Status names are case-insensitive strings: <c>Pending</c>, <c>Running</c>, <c>Suspended</c>, <c>Completed</c>,
/// <c>Failed</c>, <c>Terminated</c>, and <c>Canceled</c>. The <c>Canceled</c> status is retained for compatibility.
/// For type-safe configuration, use the <c>WithDedupeStatuses</c> extension method in the
/// <c>Microsoft.DurableTask.Client</c> namespace. The
/// <c>StartOrchestrationOptionsExtensions.ValidDedupeStatuses</c> field lists the supported statuses.
/// </para>
/// <para>
/// Do not include <c>Terminated</c> while omitting any of <c>Pending</c>, <c>Running</c>, or <c>Suspended</c>.
/// Clients that terminate active instances before replacement reject this combination with an
/// <see cref="ArgumentException"/>, because termination would make the replacement conflict with this list.
/// </para>
/// </remarks>
public IReadOnlyList<string>? DedupeStatuses { get; init; }
}
28 changes: 27 additions & 1 deletion src/Client/Core/StartOrchestrationOptionsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,34 @@
/// Creates a new <see cref="StartOrchestrationOptions"/> with the specified deduplication statuses.
/// </summary>
/// <param name="options">The base options to extend.</param>
/// <param name="dedupeStatuses">The orchestration runtime statuses that should be considered for deduplication.</param>
/// <param name="dedupeStatuses">
/// The runtime statuses that prevent scheduling a new orchestration with the same instance ID.
/// An empty array requests reuse for all statuses.
/// </param>
/// <returns>A new <see cref="StartOrchestrationOptions"/> instance with the deduplication statuses set.</returns>
/// <remarks>
/// <para>
/// If an existing instance has one of the specified statuses, scheduling fails with an
/// <see cref="DurableTask.Core.Exceptions.OrchestrationAlreadyExistsException"/>. Omitted statuses allow instance ID reuse, subject to backend

Check warning on line 45 in src/Client/Core/StartOrchestrationOptionsExtensions.cs

View workflow job for this annotation

GitHub Actions / dts-emulator-tests

XML comment has cref attribute 'OrchestrationAlreadyExistsException' that could not be resolved

Check warning on line 45 in src/Client/Core/StartOrchestrationOptionsExtensions.cs

View workflow job for this annotation

GitHub Actions / smoke-tests

XML comment has cref attribute 'OrchestrationAlreadyExistsException' that could not be resolved

Check warning on line 45 in src/Client/Core/StartOrchestrationOptionsExtensions.cs

View workflow job for this annotation

GitHub Actions / build

XML comment has cref attribute 'OrchestrationAlreadyExistsException' that could not be resolved
/// support and configuration. Reusing an active instance ID can terminate the existing orchestration.
/// See <see cref="StartOrchestrationOptions.DedupeStatuses"/> for default behavior and configuration restrictions,
/// and <see cref="ValidDedupeStatuses"/> for the supported statuses.
/// </para>
/// <para>
/// This method returns a copy and does not modify <paramref name="options"/>. Calling it without statuses
/// sets an empty list rather than restoring the <see langword="null"/> backend default.
/// </para>
/// </remarks>
/// <example>
/// Prevent replacement of active instances while allowing instance ID reuse after completion, failure, or termination:
/// <code>
/// StartOrchestrationOptions options = new StartOrchestrationOptions("singleton-workflow")
/// .WithDedupeStatuses(
/// OrchestrationRuntimeStatus.Pending,
/// OrchestrationRuntimeStatus.Running,
/// OrchestrationRuntimeStatus.Suspended);
/// </code>
/// </example>
public static StartOrchestrationOptions WithDedupeStatuses(
this StartOrchestrationOptions options,
params OrchestrationRuntimeStatus[] dedupeStatuses)
Expand Down
Loading