Temporal Signal
A Signal is a message sent asynchronously to a running Workflow Execution which can be used to change the state and control the flow of a Workflow Execution. It can only deliver data to a Workflow Execution that has not already closed. To receive data from a Workflow, you can use a Query.
Signals are useful for Workflows that must react to external events, like waiting for a human to complete a task. Here are some other examples of when a developer might use a Signal in a Temporal application:
- User Interactions in Web Applications: An online retailer that uses Temporal Workflows to manage orders could use Signals to let customers modify them. For example, when a user clicks a link or button to change the delivery date, the web application could use a Signal to pass the new date to the Workflow Execution responsible for processing that order.
- Processing Events: In an order processing system, a Signal can be sent to a Workflow when payment is confirmed, allowing the Workflow to proceed with the next steps like shipping the order.
- Frequently Changing Data Interactions: Workflows that depend on frequently changing data, such as stock trading dashboards, can use Signals to receive the latest data and then take a different execution path based on this data.
Signals are a powerful feature in Temporal, enabling Workflows to be dynamic and responsive to external events.
Developing Signals
There are two steps for adding support for a Signal to your Workflow code:
- Defining the Signal - You define the method within the Workflow Interface which specifies the name, return type, and parameters passed via the Signal.
- Handling the Signal - You implement the method that will be invoked when the Signal is received from a Temporal Client.
import io.temporal.workflow.WorkflowInterface;
import io.temporal.workflow.WorkflowMethod;
import io.temporal.workflow.SignalMethod;
@WorkflowInterface
public interface MyWorkflow {
@WorkflowMethod
String workflow(String input);
@SignalMethod
void joinSignal(String userID, String groupID);
}
Waiting for Signals
Signals in Temporal allow Workflows to react to external events without disrupting their ongoing execution - that is, Signals are not designed to necessarily block the overall Workflow Execution.
The Workflow continues to execute its logic independently of when or whether a Signal will be received. However, if required, a Workflow can be designed to pause its execution until a specific Signal is received. This is achieved using Workflow.await()
, a method imported from the Java SDK which will stop the Workflow’s progress until a certain condition is fulfilled. Using Workflow.await()
within the body of a Workflow to pause a Workflow until a certain condition is met is a very common pattern.
Sending Signal
public class SignalClient {
public static void main(String[] args) throws Exception {
WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();
WorkflowClient client = WorkflowClient.newInstance(service);
SignalWorkflow handle = client.newWorkflowStub(SignalWorkflow.class, "workflow-id-123");
handle.joinSignal("user-1", "group-1");
System.exit(0);
}
}
From command-line:
$ temporal workflow signal \
--workflow-id="helloSignal" \
--name="updateGreeting" \
--input=\"Bye\"
From within a Workflow:
public String signalingWorkflow() {
MyWorkflow workflow = Workflow.newExternalWorkflowStub(MyWorkflow.class, "workflow-id-123");
workflow.joinSignal("user-1", "group-1");
return "Success";
}
Signal-With-Start
In some cases, you might need to send a Signal, but aren’t sure whether the Workflow Execution is running yet. The Signal-with-Start
feature in Temporal provides a solution for this.
This feature checks if there is currently a running Workflow Execution with the given Workflow ID. If the Workflow ID exists, then it will be signaled. Otherwise, a new Workflow Execution is started and immediately sent the Signal. This feature is useful in scenarios where you want to ensure that a Workflow is running and that it receives specific information right from the beginning.
Signal-With-Start
is a Client method that takes the following arguments:
- Workflow type
- A Workflow ID
- Workflow input
- A Signal type
- Signal input
- Task Queue
import io.temporal.client.BatchRequest;
...
public static void signalWithStart() {
WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();
WorkflowClient client = WorkflowClient.newInstance(service);
WorkflowOptions optons = WorkflowOptions.newBuilder()
.setWorkflowId("workflow-id-123")
.setTaskQueue("my-taskqueue")
.build();
MyWorkflow workflow = client.newWorkflowStub(MyWorkflow.class, options);
BatchRequest request = client.newSignalWithStartRequest();
request.add(workflow::joinSignal, "user-1", "group-1");
client.signalWithStart(request);
}