Skip to content

Commit b9458fc

Browse files
Wire reason parameter in workflow cancellation request (#2519)
1 parent e8d9fda commit b9458fc

File tree

8 files changed

+58
-4
lines changed

8 files changed

+58
-4
lines changed

temporal-sdk/src/main/java/io/temporal/client/WorkflowStub.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,21 @@ <R> CompletableFuture<R> getResultAsync(
369369
*/
370370
void cancel();
371371

372+
/**
373+
* Request cancellation of a workflow execution with a reason.
374+
*
375+
* <p>Cancellation cancels {@link io.temporal.workflow.CancellationScope} that wraps the main
376+
* workflow method. Note that workflow can take long time to get canceled or even completely
377+
* ignore the cancellation request.
378+
*
379+
* @param reason optional reason for the cancellation request
380+
* @throws WorkflowNotFoundException if the workflow execution doesn't exist or is already
381+
* completed
382+
* @throws WorkflowServiceException for all other failures including networking and service
383+
* availability issues
384+
*/
385+
void cancel(@Nullable String reason);
386+
372387
/**
373388
* Terminates a workflow execution.
374389
*

temporal-sdk/src/main/java/io/temporal/client/WorkflowStubImpl.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,10 +429,16 @@ public <R> WorkflowUpdateHandle<R> getUpdateHandle(
429429

430430
@Override
431431
public void cancel() {
432+
cancel(null);
433+
}
434+
435+
@Override
436+
public void cancel(@Nullable String reason) {
432437
checkStarted();
433438
WorkflowExecution targetExecution = currentExecutionWithoutRunId();
434439
try {
435-
workflowClientInvoker.cancel(new WorkflowClientCallsInterceptor.CancelInput(targetExecution));
440+
workflowClientInvoker.cancel(
441+
new WorkflowClientCallsInterceptor.CancelInput(targetExecution, reason));
436442
} catch (Exception e) {
437443
Throwable failure = throwAsWorkflowFailureException(e, targetExecution);
438444
throw new WorkflowServiceException(targetExecution, workflowType.orElse(null), failure);

temporal-sdk/src/main/java/io/temporal/common/interceptors/WorkflowClientCallsInterceptor.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,14 +398,30 @@ public R getResult() {
398398

399399
final class CancelInput {
400400
private final WorkflowExecution workflowExecution;
401+
private final @Nullable String reason;
401402

403+
/**
404+
* @deprecated Use {@link #CancelInput(WorkflowExecution, String)} to provide a cancellation
405+
* reason instead.
406+
*/
407+
@Deprecated
402408
public CancelInput(WorkflowExecution workflowExecution) {
409+
this(workflowExecution, null);
410+
}
411+
412+
public CancelInput(WorkflowExecution workflowExecution, @Nullable String reason) {
403413
this.workflowExecution = workflowExecution;
414+
this.reason = reason;
404415
}
405416

406417
public WorkflowExecution getWorkflowExecution() {
407418
return workflowExecution;
408419
}
420+
421+
@Nullable
422+
public String getReason() {
423+
return reason;
424+
}
409425
}
410426

411427
final class StartUpdateInput<R> {

temporal-sdk/src/main/java/io/temporal/internal/client/RootWorkflowClientInvoker.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,9 @@ public CancelOutput cancel(CancelInput input) {
630630
.setWorkflowExecution(input.getWorkflowExecution())
631631
.setNamespace(clientOptions.getNamespace())
632632
.setIdentity(clientOptions.getIdentity());
633+
if (input.getReason() != null) {
634+
request.setReason(input.getReason());
635+
}
633636
genericClient.requestCancel(request.build());
634637
return new CancelOutput();
635638
}

temporal-sdk/src/test/java/io/temporal/workflow/cancellationTests/WorkflowAwaitCancellationTest.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.temporal.client.WorkflowFailedException;
1111
import io.temporal.client.WorkflowStub;
1212
import io.temporal.failure.CanceledFailure;
13+
import io.temporal.internal.common.WorkflowExecutionUtils;
1314
import io.temporal.testing.internal.SDKTestWorkflowRule;
1415
import io.temporal.workflow.Workflow;
1516
import io.temporal.workflow.shared.TestWorkflows;
@@ -29,7 +30,7 @@ public void awaitCancellation() {
2930
execution = WorkflowClient.start(workflow::execute, "input1");
3031
try {
3132
WorkflowStub untyped = WorkflowStub.fromTyped(workflow);
32-
untyped.cancel();
33+
untyped.cancel("test reason");
3334
untyped.getResult(String.class);
3435
fail("unreacheable");
3536
} catch (WorkflowFailedException e) {
@@ -42,6 +43,13 @@ public void awaitCancellation() {
4243
"WorkflowExecutionCancelled event is expected",
4344
EventType.EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED,
4445
lastEvent.getEventType());
46+
assertEquals(
47+
"WorkflowExecutionCancelled event should have the correct cause",
48+
"test reason",
49+
WorkflowExecutionUtils.getEventOfType(
50+
history, EventType.EVENT_TYPE_WORKFLOW_EXECUTION_CANCEL_REQUESTED)
51+
.getWorkflowExecutionCancelRequestedEventAttributes()
52+
.getCause());
4553
}
4654
}
4755

temporal-sdk/src/test/java/io/temporal/workflow/cancellationTests/WorkflowAwaitWithDurationCancellationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public void awaitWithDurationCancellation() throws InterruptedException {
3535
try {
3636
WorkflowStub untyped = WorkflowStub.fromTyped(workflow);
3737
workflowStarted.waitForSignal();
38-
untyped.cancel();
38+
untyped.cancel("reason");
3939
untyped.getResult(String.class);
4040
fail("unreacheable");
4141
} catch (WorkflowFailedException e) {

temporal-test-server/src/main/java/io/temporal/internal/testservice/StateMachines.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1488,7 +1488,8 @@ private static void requestWorkflowCancellation(
14881488
long notUsed) {
14891489
WorkflowExecutionCancelRequestedEventAttributes.Builder a =
14901490
WorkflowExecutionCancelRequestedEventAttributes.newBuilder()
1491-
.setIdentity(cancelRequest.getIdentity());
1491+
.setIdentity(cancelRequest.getIdentity())
1492+
.setCause(cancelRequest.getReason());
14921493
HistoryEvent cancelRequested =
14931494
HistoryEvent.newBuilder()
14941495
.setEventType(EventType.EVENT_TYPE_WORKFLOW_EXECUTION_CANCEL_REQUESTED)

temporal-testing/src/main/java/io/temporal/testing/TimeLockingInterceptor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,11 @@ public void cancel() {
164164
next.cancel();
165165
}
166166

167+
@Override
168+
public void cancel(@Nullable String reason) {
169+
next.cancel(reason);
170+
}
171+
167172
@Override
168173
public void terminate(@Nullable String reason, Object... details) {
169174
next.terminate(reason, details);

0 commit comments

Comments
 (0)