Skip to content

Commit a11af70

Browse files
authored
Rewind client api (#84)
* rewind instance * feedback
1 parent 480eb2d commit a11af70

File tree

3 files changed

+308
-0
lines changed

3 files changed

+308
-0
lines changed

package-lock.json

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/durabletask-js/src/client/client.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,64 @@ export class TaskHubGrpcClient {
305305
);
306306
}
307307

308+
/**
309+
* Rewinds a failed orchestration instance to a previous state to allow it to retry from the point of failure.
310+
*
311+
* This method is used to "rewind" a failed orchestration back to its last known good state, allowing it
312+
* to be replayed from that point. This is particularly useful for recovering from transient failures
313+
* or for debugging purposes.
314+
*
315+
* Only orchestration instances in the `Failed` state can be rewound.
316+
*
317+
* @param instanceId - The unique identifier of the orchestration instance to rewind.
318+
* @param reason - A reason string describing why the orchestration is being rewound.
319+
* @throws {Error} If the orchestration instance is not found.
320+
* @throws {Error} If the orchestration instance is in a state that does not allow rewinding.
321+
* @throws {Error} If the rewind operation is not supported by the backend.
322+
*/
323+
async rewindInstance(instanceId: string, reason: string): Promise<void> {
324+
if (!instanceId) {
325+
throw new Error("instanceId is required");
326+
}
327+
328+
const req = new pb.RewindInstanceRequest();
329+
req.setInstanceid(instanceId);
330+
331+
if (reason) {
332+
const reasonValue = new StringValue();
333+
reasonValue.setValue(reason);
334+
req.setReason(reasonValue);
335+
}
336+
337+
console.log(`Rewinding '${instanceId}' with reason: ${reason}`);
338+
339+
try {
340+
await callWithMetadata<pb.RewindInstanceRequest, pb.RewindInstanceResponse>(
341+
this._stub.rewindInstance.bind(this._stub),
342+
req,
343+
this._metadataGenerator,
344+
);
345+
} catch (e) {
346+
// Handle gRPC errors and convert them to appropriate errors
347+
if (e && typeof e === "object" && "code" in e) {
348+
const grpcError = e as { code: number; details?: string };
349+
if (grpcError.code === grpc.status.NOT_FOUND) {
350+
throw new Error(`An orchestration with the instanceId '${instanceId}' was not found.`);
351+
}
352+
if (grpcError.code === grpc.status.FAILED_PRECONDITION) {
353+
throw new Error(grpcError.details || `Cannot rewind orchestration '${instanceId}': it is in a state that does not allow rewinding.`);
354+
}
355+
if (grpcError.code === grpc.status.UNIMPLEMENTED) {
356+
throw new Error(grpcError.details || `The rewind operation is not supported by the backend.`);
357+
}
358+
if (grpcError.code === grpc.status.CANCELLED) {
359+
throw new Error(`The rewind operation for '${instanceId}' was cancelled.`);
360+
}
361+
}
362+
throw e;
363+
}
364+
}
365+
308366
/**
309367
* Restarts an existing orchestration instance with its original input.
310368
*

0 commit comments

Comments
 (0)