Java and Go use very different approaches for cancellation. The reason is that Go has a standard mechanism to cancel function execution through Context. Java doesn’t have any standard way to cancel the execution of some code. Thread interrupt and the related exceptions are more like an annoyance than a real cancellation mechanism.
Go
Go uses workflow.Context
for cancellation. workflow.Context
behaves exactly as the standard context.Context
with the only difference is that Done()
method returns the Channel
interface instead of the native Go channel. To cancel an activity for example cancel the context that was used to invoke that activity:
ctx, cancelFunc := workflow.WithCancel(ctx)
err := workflow.ExecuteActivity(ctx, foo).Get(null)
if _, ok := err.(*CanceledError); ok {
// Activity was canceled
}
...
// Trigger cancellation of activity context from a different goroutine
cancelFunc()
Java
As Java doesn’t have any standard patterns around code cancellation Temporal introduced its own mechanism. Any part of the workflow code can be made cancellable by wrapping it in a CancellationScope. Then call to CancellationScope.cancel()
will cancel the wrapped code.
CancellationScope scope = Workflow.newCancellationScope(() -> {
try {
activity.foo())
} catch (CanceledFailure e) {
// Activity was canceled
}
});
scope.run();
...
// From a different thread (for example signal method):
scope.cancel();
Workflow Cancellation
The above samples demonstrate explicit cancellation requested by the workflow code itself. What does happen when a whole workflow gets a cancellation request through tctl
or any other client?
In the case of Go, the root workflow context passed to the workflow function gets canceled.
In the case of Java, the root CancellationScope
is canceled. The main workflow method (annotated with @WorflowMethod
) is always invoked in the context of a root CancellationScope
. So any cancellable code in the workflow gets notified when the root context is canceled.
Cleanup
It is not possible to execute any activities using a canceled context in the case of Go. They will immediately fail with CanceledError.
It is also not possible to invoke any activities inside a canceled CancellationScope
in the case of Java. Any such invocation will immediately throw CanceledFailure
.
This represents a problem that canceled workflow cannot execute any cleanup logic that executes activities. The solution is to use a disconnected context in Go (created through workflow.NewDisconnectedContext
) and a disconnected cancellation scope (created through Workflow.newDetachedCancellationScope
) in Java for such cleanup operations.