diff --git a/Directory.Packages.props b/Directory.Packages.props
index d5300050..a3e580b8 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -30,7 +30,7 @@
-
+
diff --git a/eng/targets/Release.props b/eng/targets/Release.props
index d4adbf24..41429536 100644
--- a/eng/targets/Release.props
+++ b/eng/targets/Release.props
@@ -17,7 +17,7 @@
- 1.16.2
+ 1.17.0
diff --git a/src/Client/Core/DurableTaskClient.cs b/src/Client/Core/DurableTaskClient.cs
index 8ad6181e..f2d658d8 100644
--- a/src/Client/Core/DurableTaskClient.cs
+++ b/src/Client/Core/DurableTaskClient.cs
@@ -436,7 +436,35 @@ public virtual Task 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.");
+
+ ///
+ /// Rewinds the specified orchestration instance by re-executing any failed Activities, and recursively rewinding
+ /// any failed suborchestrations with failed Activities.
+ ///
+ ///
+ /// The orchestration's history will be replaced with a new history that excludes the failed Activities and suborchestrations,
+ /// 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.
+ ///
+ /// The instance ID of the orchestration to rewind.
+ /// The reason for the rewind.
+ /// 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.
+ /// A task that represents the enqueueing of the rewind operation.
+ /// Thrown if this implementation of does not
+ /// support rewinding orchestrations.
+ /// Thrown if the backend storage provider does not support rewinding orchestrations.
+ /// Thrown if an orchestration with the specified does not exist.
+ /// Thrown if a precondition of the operation fails, for example if the specified
+ /// orchestration is not in a "Failed" state.
+ /// Thrown if the operation is canceled via the token.
+ public virtual Task RewindInstanceAsync(
+ string instanceId,
+ string reason,
+ CancellationToken cancellation = default)
+ => throw new NotSupportedException($"{this.GetType()} does not support orchestration rewind.");
// TODO: Create task hub
diff --git a/src/Client/Grpc/GrpcDurableTaskClient.cs b/src/Client/Grpc/GrpcDurableTaskClient.cs
index c57b22f9..efc6d765 100644
--- a/src/Client/Grpc/GrpcDurableTaskClient.cs
+++ b/src/Client/Grpc/GrpcDurableTaskClient.cs
@@ -431,6 +431,43 @@ public override async Task RestartAsync(
}
}
+ ///
+ 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)
diff --git a/src/Grpc/orchestrator_service.proto b/src/Grpc/orchestrator_service.proto
index b2def087..7e38f67e 100644
--- a/src/Grpc/orchestrator_service.proto
+++ b/src/Grpc/orchestrator_service.proto
@@ -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;
@@ -251,6 +258,7 @@ message HistoryEvent {
EntityLockRequestedEvent entityLockRequested = 27;
EntityLockGrantedEvent entityLockGranted = 28;
EntityUnlockSentEvent entityUnlockSent = 29;
+ ExecutionRewoundEvent executionRewound = 30;
}
}
diff --git a/src/Grpc/versions.txt b/src/Grpc/versions.txt
index 709a8007..9f3e2a16 100644
--- a/src/Grpc/versions.txt
+++ b/src/Grpc/versions.txt
@@ -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
diff --git a/src/Shared/Grpc/ProtoUtils.cs b/src/Shared/Grpc/ProtoUtils.cs
index 13b64fe8..ac31aea4 100644
--- a/src/Shared/Grpc/ProtoUtils.cs
+++ b/src/Shared/Grpc/ProtoUtils.cs
@@ -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.");
}