Skip to content
Merged
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
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

<!-- DurableTask Packages -->
<ItemGroup>
<PackageVersion Include="Microsoft.Azure.DurableTask.Core" Version="3.5.1" />
<PackageVersion Include="Microsoft.Azure.DurableTask.Core" Version="3.6.0" />
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask" Version="1.2.2" />
</ItemGroup>

Expand Down
2 changes: 1 addition & 1 deletion eng/targets/Release.props
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
</PropertyGroup>

<PropertyGroup>
<VersionPrefix>1.16.2</VersionPrefix>
<VersionPrefix>1.17.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>

Expand Down
30 changes: 29 additions & 1 deletion src/Client/Core/DurableTaskClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,35 @@ public virtual Task<string> RestartAsync(
string instanceId,
bool restartWithNewInstanceId = false,
CancellationToken cancellation = default)
=> throw new NotSupportedException($"{this.GetType()} does not support orchestration restart.");
=> throw new NotSupportedException($"{this.GetType()} does not support orchestration restart.");

/// <summary>
/// Rewinds the specified orchestration instance by re-executing any failed Activities, and recursively rewinding
/// any failed suborchestrations with failed Activities.
/// </summary>
/// <remarks>
/// The orchestration's history will be replaced with a new history that excludes the failed Activities and suborchestrations,
Comment thread
sophiatev marked this conversation as resolved.
/// and a new execution ID will be generated for the rewound orchestration instance. As the failed Activities and suborchestrations
/// re-execute, the history will be appended with new TaskScheduled, TaskCompleted, and SubOrchestrationInstanceCompleted events.
/// Note that only orchestrations in a "Failed" state can be rewound.
/// </remarks>
/// <param name="instanceId">The instance ID of the orchestration to rewind.</param>
/// <param name="reason">The reason for the rewind.</param>
/// <param name="cancellation">The cancellation token. This only cancels enqueueing the rewind request to the backend.
/// It does not abort rewinding the orchestration once the request has been enqueued.</param>
/// <returns>A task that represents the enqueueing of the rewind operation.</returns>
/// <exception cref="NotSupportedException">Thrown if this implementation of <see cref="DurableTaskClient"/> does not
/// support rewinding orchestrations.</exception>
/// <exception cref="NotImplementedException">Thrown if the backend storage provider does not support rewinding orchestrations.</exception>
/// <exception cref="ArgumentException">Thrown if an orchestration with the specified <paramref name="instanceId"/> does not exist.</exception>
/// <exception cref="InvalidOperationException">Thrown if a precondition of the operation fails, for example if the specified
/// orchestration is not in a "Failed" state.</exception>
/// <exception cref="OperationCanceledException">Thrown if the operation is canceled via the <paramref name="cancellation"/> token.</exception>
public virtual Task RewindInstanceAsync(
Comment thread
sophiatev marked this conversation as resolved.
string instanceId,
string reason,
CancellationToken cancellation = default)
=> throw new NotSupportedException($"{this.GetType()} does not support orchestration rewind.");

// TODO: Create task hub

Expand Down
37 changes: 37 additions & 0 deletions src/Client/Grpc/GrpcDurableTaskClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,43 @@ public override async Task<string> RestartAsync(
}
}

/// <inheritdoc/>
public override async Task RewindInstanceAsync(
string instanceId,
string reason,
CancellationToken cancellation = default)
{
Check.NotNullOrEmpty(instanceId);
Check.NotEntity(this.options.EnableEntitySupport, instanceId);

var request = new P.RewindInstanceRequest
{
InstanceId = instanceId,
Reason = reason,
};
try
{
await this.sidecarClient.RewindInstanceAsync(request, cancellationToken: cancellation);
}
catch (RpcException e) when (e.StatusCode == StatusCode.NotFound)
{
throw new ArgumentException($"An orchestration with the instanceId {instanceId} was not found.", e);
}
catch (RpcException e) when (e.StatusCode == StatusCode.FailedPrecondition)
{
throw new InvalidOperationException(e.Status.Detail);
}
catch (RpcException e) when (e.StatusCode == StatusCode.Unimplemented)
{
throw new NotImplementedException(e.Status.Detail);
}
catch (RpcException e) when (e.StatusCode == StatusCode.Cancelled)
{
throw new OperationCanceledException(
$"The {nameof(this.RewindInstanceAsync)} operation was canceled.", e, cancellation);
}
}

static AsyncDisposable GetCallInvoker(GrpcDurableTaskClientOptions options, out CallInvoker callInvoker)
{
if (options.Channel is GrpcChannel c)
Expand Down
8 changes: 8 additions & 0 deletions src/Grpc/orchestrator_service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,13 @@ message EntityLockGrantedEvent {
string criticalSectionId = 1;
}

message ExecutionRewoundEvent {
google.protobuf.StringValue reason = 1;
google.protobuf.StringValue parentExecutionId = 2; // used only for rewinding suborchestrations, null otherwise
google.protobuf.StringValue instanceId = 3; // used only for rewinding suborchestrations, null otherwise
TraceContext parentTraceContext = 4; // used only for rewinding suborchestrations, null otherwise
}

message HistoryEvent {
int32 eventId = 1;
google.protobuf.Timestamp timestamp = 2;
Expand Down Expand Up @@ -251,6 +258,7 @@ message HistoryEvent {
EntityLockRequestedEvent entityLockRequested = 27;
EntityLockGrantedEvent entityLockGranted = 28;
EntityUnlockSentEvent entityUnlockSent = 29;
ExecutionRewoundEvent executionRewound = 30;
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/Grpc/versions.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# The following files were downloaded from branch main at 2025-10-13 18:06:55 UTC
https://raw.githubusercontent.com/microsoft/durabletask-protobuf/97cf9cf6ac44107b883b0f4ab1dd62ee2332cfd9/protos/orchestrator_service.proto
# The following files were downloaded from branch main at 2025-11-04 03:02:50 UTC
https://raw.githubusercontent.com/microsoft/durabletask-protobuf/73592a81dac1b5dd8653bb5ed1099aee2c89e3a2/protos/orchestrator_service.proto
3 changes: 3 additions & 0 deletions src/Shared/Grpc/ProtoUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ internal static HistoryEvent ConvertHistoryEvent(P.HistoryEvent proto, EntityCon
Tags = proto.HistoryState.OrchestrationState.Tags,
});
break;
case P.HistoryEvent.EventTypeOneofCase.ExecutionRewound:
historyEvent = new ExecutionRewoundEvent(proto.EventId);
break;
default:
throw new NotSupportedException($"Deserialization of {proto.EventTypeCase} is not supported.");
}
Expand Down
Loading