Java example/sample for activity cancellation in response to a signal?

Hello! I’m trying to understand if the following is possible and supported with Temporal:

I would like to build a workflow which kicks off a long-running activity (e.g. ActivityExecutionContext.doNotCompleteOnReturn()). The activity is managing some local process (irrelevant I think to this question), and heartbeats probably 1x/second. We’ll probably have ~200k of these workflows running.

While the activity is running, we may receive a signal that changes some information about the “work” the activity is doing.

Ideally, I’d love the signal to somehow notify the activity itself about this new data, but I don’t believe this is possible. However, I read here: What is an Activity Execution? | Temporal Documentation

A Workflow can request to cancel an Activity Execution. When an Activity Execution is canceled, or its Workflow Execution has completed or failed, the context passed into its function is canceled, which also sets its channel’s closed state to Done . An Activity can use that to perform any necessary cleanup and abort its execution.

My interpretation of the above is that the workflow (or signal?) can request to cancel the activity, at which point the heartbeat would fail and the activity can perform any necessary cleanup. I assume I can then have an opportunity to invoke the activity again from the workflow with this new information I received from the signal.

I could not find any example of how to request activity cancellation from a workflow / signal in the docs for Java. I did a little searching and read some similar posts, but not quite showing me how to do what I’m describing:

  1. Asynchronous activity cancellation (Java SDK)
  2. Problems cancelling a running activity from parent workflow (Go SDK)

My questions are:

  1. Is what I’m describing possible and supported? And in the Java SDK?
  2. Can you please share a pointer to how I would invoke the activity cancellation from the signal code?

Thank you!
Dan

Hey @dpincas,

I believe this is what you were referring to:

Java doesn’t have a commonly used way to cancel computation besides a very unfriendly Thread. interrupt. So the Temporal framework uses its own CancellationScope abstraction. The code inside a CancellationScope is canceled when cancel is called on the surrounding scope. The main workflow method is always invoked in the context of a root cancellation scope. And this root scope is canceled when a workflow is canceled.

The behavior of an activity invocation is controlled through ActivityOptions.cancellationType property. It can have three values: WAIT_CANCELLATION_COMPLETED, TRY_CANCEL, ABANDON. The WAIT_CANCELLATION_COMPLETED blocks the activity invocation until the activity is canceled or completed, TRY_CANCEL sends cancellation to the activity, but immediately fails its invocation in the workflow code and ABANDON doesn’t send cancellation and immediately fails it in the workflow code. For an activity to get canceled it must heartbeat as heart beating is the only way at this point to send information to an already executing activity.

1 Like

The child workflow has similar ChildWorkflowOptions.cancellationType property with the similar behavior.

1 Like

Just to add to @jreynolds7 answer, here is a small standalone demo that shows activity cancellation from signal handler (with activity cleanup). Its very similar to HelloCancellationScope in samples-java repo.

would like to build a workflow which kicks off a long-running activity (e.g. ActivityExecutionContext.doNotCompleteOnReturn() )

Not sure why you want to use ActivityCompletionClient, can you explain?

2 Likes

Thank you both! I wasn’t sure if I could reference the cancellation scope from my signal method. @tihomir your example spells things out very clearly.

I do not need to, but it seemed simpler for my use-case. Referencing the docs here, I assumed we could indicate that the activity lifecycle is long-running (longer than just the method invocation) by calling ActivityExecutionContext.doNotCompleteOnReturn() so that the activity method could return and it would not complete. In my case, the activity is responsible primarily for registering some information on a separate thread in the Java process and heartbeating. When the heartbeat fails (e.g. worker death, long GC, or cancellation received from the workflow), it would unregister/cleanup.

Are you suggesting that it would be better to not use ActivityExecutionContext.doNotCompleteOnReturn() and instead simply loop/Thread.sleep() in the activity method? If so, can you share your reasons so I can understand?

Thanks again!