Skip to content

Commit 1a96c2c

Browse files
authored
Adding rewind client API (#253)
1 parent ebebbc9 commit 1a96c2c

File tree

11 files changed

+743
-14
lines changed

11 files changed

+743
-14
lines changed

.github/workflows/build-validation.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,8 @@ jobs:
156156
- name: Archive test report
157157
uses: actions/upload-artifact@v4
158158
with:
159-
name: Integration test report
160-
path: client/build/reports/tests/endToEndTest
159+
name: E2E test report
160+
path: endtoendtests/build/reports/tests/endToEndTest
161161

162162
functions-sample-tests:
163163

@@ -195,5 +195,5 @@ jobs:
195195
- name: Archive test report
196196
uses: actions/upload-artifact@v4
197197
with:
198-
name: Integration test report
199-
path: client/build/reports/tests/endToEndTest
198+
name: Functions Sample test report
199+
path: samples-azure-functions/build/reports/tests/sampleTest

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## Unreleased
22

3+
* Adding rewind client API ([#253](https://github.com/microsoft/durabletask-java/pull/253)). Note: orchestration processing for rewind is supported with Azure Functions but not with the standalone `GrpcDurableTaskWorker`.
4+
35
## v1.7.0
46
* Add descriptive error when orchestration type is not registered ([#261](https://github.com/microsoft/durabletask-java/pull/261))
57
* Update all dependencies to latest versions ([#260](https://github.com/microsoft/durabletask-java/pull/260))

azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/HttpManagementPayload.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public class HttpManagementPayload {
1313
private final String id;
1414
private final String purgeHistoryDeleteUri;
1515
private final String restartPostUri;
16+
private final String rewindPostUri;
1617
private final String sendEventPostUri;
1718
private final String statusQueryGetUri;
1819
private final String terminatePostUri;
@@ -33,6 +34,7 @@ public HttpManagementPayload(
3334
this.id = instanceId;
3435
this.purgeHistoryDeleteUri = instanceStatusURL + "?" + requiredQueryStringParameters;
3536
this.restartPostUri = instanceStatusURL + "/restart?" + requiredQueryStringParameters;
37+
this.rewindPostUri = instanceStatusURL + "/rewind?reason={text}&" + requiredQueryStringParameters;
3638
this.sendEventPostUri = instanceStatusURL + "/raiseEvent/{eventName}?" + requiredQueryStringParameters;
3739
this.statusQueryGetUri = instanceStatusURL + "?" + requiredQueryStringParameters;
3840
this.terminatePostUri = instanceStatusURL + "/terminate?reason={text}&" + requiredQueryStringParameters;
@@ -94,4 +96,13 @@ public String getRestartPostUri() {
9496
return restartPostUri;
9597
}
9698

99+
/**
100+
* Gets the HTTP POST instance rewind endpoint.
101+
*
102+
* @return The HTTP URL for posting instance rewind commands.
103+
*/
104+
public String getRewindPostUri() {
105+
return rewindPostUri;
106+
}
107+
97108
}

client/src/main/java/com/microsoft/durabletask/DurableTaskClient.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,41 @@ public abstract OrchestrationMetadata waitForInstanceCompletion(
292292
*/
293293
public abstract String restartInstance(String instanceId, boolean restartWithNewInstanceId);
294294

295+
/**
296+
* Rewinds a failed orchestration instance to the last known good state and replays from there.
297+
* <p>
298+
* This method can only be used on orchestration instances that are in a <code>Failed</code> state.
299+
* When rewound, the orchestration instance will restart from the point of failure as if the failure
300+
* never occurred. It rewinds the orchestration by replaying any
301+
* Failed Activities and Failed suborchestrations that themselves have Failed Activities
302+
* <p>
303+
* <b>Note:</b> Rewind requires a backend that supports it. When using Azure Functions with the
304+
* Durable Task extension, rewind is fully supported. The standalone {@code GrpcDurableTaskWorker}
305+
* does not currently support orchestration processing for rewind.
306+
*
307+
* @param instanceId the ID of the orchestration instance to rewind
308+
*/
309+
public void rewindInstance(String instanceId) {
310+
this.rewindInstance(instanceId, null);
311+
}
312+
313+
/**
314+
* Rewinds a failed orchestration instance to the last known good state and replays from there.
315+
* <p>
316+
* This method can only be used on orchestration instances that are in a <code>Failed</code> state.
317+
* When rewound, the orchestration instance will restart from the point of failure as if the failure
318+
* never occurred. It rewinds the orchestration by replaying any
319+
* Failed Activities and Failed suborchestrations that themselves have Failed Activities
320+
* <p>
321+
* <b>Note:</b> Rewind requires a backend that supports it. When using Azure Functions with the
322+
* Durable Task extension, rewind is fully supported. The standalone {@code GrpcDurableTaskWorker}
323+
* does not currently support orchestration processing for rewind.
324+
*
325+
* @param instanceId the ID of the orchestration instance to rewind
326+
* @param reason the reason for rewinding the orchestration instance
327+
*/
328+
public abstract void rewindInstance(String instanceId, @Nullable String reason);
329+
295330
/**
296331
* Suspends a running orchestration instance.
297332
* @param instanceId the ID of the orchestration instance to suspend

client/src/main/java/com/microsoft/durabletask/DurableTaskGrpcClient.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,29 @@ public void resumeInstance(String instanceId, @Nullable String reason) {
363363
this.sidecarClient.resumeInstance(resumeRequestBuilder.build());
364364
}
365365

366+
@Override
367+
public void rewindInstance(String instanceId, @Nullable String reason) {
368+
Helpers.throwIfArgumentNull(instanceId, "instanceId");
369+
RewindInstanceRequest.Builder rewindRequestBuilder = RewindInstanceRequest.newBuilder();
370+
rewindRequestBuilder.setInstanceId(instanceId);
371+
if (reason != null) {
372+
rewindRequestBuilder.setReason(StringValue.of(reason));
373+
}
374+
try {
375+
this.sidecarClient.rewindInstance(rewindRequestBuilder.build());
376+
} catch (StatusRuntimeException e) {
377+
if (e.getStatus().getCode() == Status.Code.NOT_FOUND) {
378+
throw new IllegalArgumentException(
379+
"No orchestration instance with ID '" + instanceId + "' was found.", e);
380+
}
381+
if (e.getStatus().getCode() == Status.Code.FAILED_PRECONDITION) {
382+
throw new IllegalStateException(
383+
"Orchestration instance '" + instanceId + "' is not in a failed state and cannot be rewound.", e);
384+
}
385+
throw e;
386+
}
387+
}
388+
366389
@Override
367390
public String restartInstance(String instanceId, boolean restartWithNewInstanceId) {
368391
OrchestrationMetadata metadata = this.getInstanceMetadata(instanceId, true);

0 commit comments

Comments
 (0)