Signals Vs ActivityCompletionClient

Hi Team,

Consider a scenario, where we have a workflow with 3 activities, all of them have to happen sequentially one after the other.

In most of the temporal samples that I referred to, to achieve a requirement such as this we use Signal methods where the flow would be as below:

Workflow → Initiate Activity A → workflow blocked awaiting on a flag → On completion, Activity A signals where in we set the flag to True → workflow unblocked → continues with second activity.

Instead of signals, can’t we use returned results from activities.
In this case, the flow would be as below:

Workflow → Initiate Activity A (method returning a POJO) → workflow blocked awaiting on a boolean variable in the POJO → Activity A completes returns the POJO with the boolean variable set → workflow unblocked → continues second activity.

Q1: Is there any specific issue with the second approach? What advantages do signals offer exclusively?
From the documentation I understood 2 purposes of signals.

  1. Changing the course of workflow
  2. Bringing in new data into the workflow.

Q2: To find if an activity is done executing its task, other than signals another alternative is to use ActivityCompletionClient. So in what type of use-cases are Signals preferred to ActivityCompletionClient and vice-versa. Are Signals for sequential activities, whereas ActivityCompletionClient for async ones?

Q1: Is there any specific issue with the second approach? What advantages do signals offer exclusively?

Yes you can make data-based decisions based on activity results. Signals would allow you to for example wait on an external event before continuing your workflow execution. In your code you could invoke the activities sync blocking until it completes (or fails). If it completes you can then make a decision to run your next activity based on the results you got.

Q2: To find if an activity is done executing its task, other than signals another alternative is to use ActivityCompletionClient. So in what type of use-cases are Signals preferred to ActivityCompletionClient and vice-versa. Are Signals for sequential activities, whereas ActivityCompletionClient for async ones?

I am not sure why you need signals for sync activity invocations, could you explain your use case?
ActivityCompletionClient can be used to asyncronously complete activities, see sample here.

I think you are confusing activities with operations that are executed by external entities like humans.

If all you want is to execute three activities in a sequence then just invoke them synchronously one after another:

activities.activity1();
activities.activity2();
activities.activity3();

The activity + reply signal pattern is needed when the actual work is done by an external system. For example, an existing REST service exposes an endpoint to initiate some action and then later delivers the result through a webhook. In this case, an activity is used to call the REST endpoint and the webhook uses the signal to notify the workflow about the completion. The same applies to a human task. An activity is used to enqueue the task to an external task management system and the signal is used to notify workflow about task completion.

Q2: To find if an activity is done executing its task, other than signals another alternative is to use ActivityCompletionClient. So in what type of use-cases are Signals preferred to ActivityCompletionClient and vice-versa. Are Signals for sequential activities, whereas ActivityCompletionClient for async ones?

In some cases the actual activity implementation is asynchronous. For example, you want to use a custom separate thread pool to execute an activity. Then you can disconnect the activity from its original function (through Activity.getExecutionContext().doNotCompleteOnReturn) and later complete it asynchronously through the ActivityCompletionClient. Note that in this case the timeouts and retry policy applies to the whole activity. So it is a very bad fit for manual tasks which can take days.

For async activity implementation that is local to a process, we recommend using ActivityExecutionContext.useLocalManualCompletion as it is much simpler to use than doNotCompleteOnReturn.

@tihomir / @maxim Thanks for your quick response.

My use-case is as below:

I have 3 micro services. A workflow with 4 activities, lets say A,B,C,D. These activities need to execute in sequence one after the other, as they depend on previous activities output.

Service 1 - hosts the workflow. Has workflow implementation and also defines activity implementations for A, D.
Service 2 - Hosts activity implementation for B
Service 3 - Hosts activity implementation for C

Currently the flow is:

  1. based on a CRON Temporal Workflow in Service 1 is triggered
  2. Invokes Activity A in Service 1. Activity A finishes and signals workflow with an ID generated .
    Workflow unblocks.
  3. Invokes Activity B in Service 2 with this ID. Activity B finishes and signals workflow with a blob store file
    path which serves as input for next activity.
  4. Invokes Activity C in Service 3 with the file path. Activity C finishes and signals the workflow again.
  5. Workflow resumes, invokes Activity D in Service 1. Marks the status of the workflow against the
    generated ID and this concludes the workflow.

As you can see, there is no human intervention in the workflow, it’s just micro services talking to each other and completing a task at scheduled time.

I would like to understand, is this the best possible approach? am I misusing Signals or is there any other better way of handling this use-case in temporal?

I gave human action as an example because it is long running and has two separate operations. One is to add a task to an external system and the second is a human completing the task.

In your case, I would not use signals. I don’t understand why you need them in your case. Just invoke the activities directly and block the workflow until they are complete.

I guess I was totally wrong about Signals then.

“An activity is used to enqueue the task to an external task management system and the signal is used to notify workflow about task completion”
So Activity is like placing an order at a restaurant (initiating action) and Signals (delivering results) would help for constantly pushing updates about the order into the workflow like order accepted, picked, delivered. Signals avoid the overhead of polling.

Now I understood, why signals are totally unnecessary in my case.
Since there is no polling involved, all that my use-case needs is to simply call activities synchronously as below and pass on the results down in the flow.
Is my understanding correct? When you say “block the workflow until they are complete”, anything else should be handled?

public class SampleWorkflowImpl implements SampleWorkflow {
  
    @Override
     public String runWorkflow(id) {

           String resA =  ActivityAStub.run(id);
           String resB = ActivityBStub.run(resA);
           String resC = ActivityCStub.run(resB);
           String resD = ActivityDStub.run(resC);

            return resD;
     }
}

Yes, your understanding is now correct. The code sample looks fine to me.

Thanks for the confirmation @maxim