Temporal Workflow message passing - Signals, Queries, & Updates
Temporal applications can receive and provide data through messages. Temporal supports three types of messages: Signals, Queries, and Updates.
Signalsβ
What is a Signal in the context of Temporal?
A Signal is an asynchronous request to a Workflow Execution.
A Signal delivers data to a running Workflow Execution. It cannot return data to the caller; to do so, use a Query instead. The Workflow code that handles a Signal can mutate Workflow state. A Signal can be sent from a Temporal Client or a Workflow. When a Signal is sent, it is received by the Temporal Service and recorded as an Event to the Workflow Execution Event History. A successful response from the Temporal Service means that the Signal has been persisted and will be delivered at least once to the Workflow Execution.1 The next scheduled Workflow Task will contain the Signal Event.
A Signal must include a destination (Namespace and Workflow Id) and name. It can include a list of arguments.
Signal handlers are Workflow functions that listen for Signals by the Signal name. Signals are delivered in the order they are received by the Temporal Service and written to History. If multiple deliveries of a Signal would be a problem for your Workflow, add idempotency logic to your Signal handler that checks for duplicates.
Signal-With-Startβ
What is a Signal-With-Start in the context of Temporal?
Signal-With-Start is a Client method that takes the following arguments:
- A Workflow Id
- Workflow input
- A Signal name
- Signal input
If there is a running Workflow Execution with the given Workflow Id, it will be Signaled. Otherwise, a new Workflow Execution is started and immediately sent the Signal.
Queriesβ
What is a Query in the context of Temporal?
A Query is a synchronous operation that is used to get the state of a Workflow Execution. The state of a running Workflow Execution is constantly changing. You can use Queries to expose the internal Workflow Execution state to the external world. Queries are available for running or completed Workflows Executions only if the Worker is up and listening on the Task Queue.
Queries are sent from a Temporal Client to a Workflow Execution. The API call is synchronous. The Query is identified at both ends by a Query name. The Workflow must have a Query handler that is developed to handle that Query and provide data that represents the state of the Workflow Execution.
Queries are strongly consistent and are guaranteed to return the most recent state. This means that the data reflects the state of all confirmed Events that came in before the Query was sent. An Event is considered confirmed if the call creating the Event returned success. Events that are created while the Query is outstanding may or may not be reflected in the Workflow state the Query result is based on.
A Query can carry arguments to specify the data it is requesting. And each Workflow can expose data to multiple types of Queries.
A Query must never mutate the state of the Workflow Executionβthat is, Queries are read-only and cannot contain any blocking code. This means, for example, that Query handling logic cannot schedule Activity Executions.
Sending Queries to completed Workflow Executions is supported, though Query reject conditions can be configured per Query.
Stack Trace Queryβ
What is a Stack Trace Query in the context of Temporal?
In many SDKs, the Temporal Client exposes a predefined __stack_trace
Query that returns the call stack of all the threads owned by that Workflow Execution.
This is a great way to troubleshoot a Workflow Execution in production.
For example, if a Workflow Execution has been stuck at a state for longer than an expected period of time, you can send a __stack_trace
Query to return the current call stack.
The __stack_trace
Query name does not require special handling in your Workflow code.
Stack Trace Queries are available only for running Workflow Executions.
Updatesβ
What is an Update in the context of Temporal?
- In Pre-release (API is subject to change)
- Introduced in Temporal Server version 1.21
- Available in Go SDK since v1.23.0
- Available in Java SDK since v1.20.0
- Available in Python SDK since v1.4.0
- Available in .NET SDK since v0.1.0-beta2
- Available in TypeScript SDK since v1.9.0
- Available in PHP SDK since v2.8.0
An Update is a request and response from a Temporal Client to a Workflow Execution.
You can think of an Update as a synchronous, blocking call that could replace both a Signal and a Query. An update is:
- A Signal that can return a value, and has lower overhead and latency
- A Query that can mutate workflow state
The Workflow must have a function to handle the Update. The Update handler can mutate workflow state (like a Signal but unlike a Query) and return a value to the caller (like a Query but unlike a Signal). Like every bit of code in a Workflow, Update handlers must be deterministic. However, they may use all the available Workflow features, such as executing Activities and child Workflows, and waiting on timers/conditions.
To avoid losing Updates when using Continue-As-New, ensure that all Update handlers have completed before calling Continue-As-New.
When there is the potential for multiple Updates to cause a duplication problem, Temporal recommends adding idempotency logic to your Update handler that checks for duplicates.
An Update has four phases.
- Admission. The Temporal Service first validates Update submissions against the configured resource usage limits. For example, limits apply to concurrent requests and requests per second. For more details, see the Temporal Platform defaults. When this phase is complete, the Platform changes the status of the Update to Admitted. At this stage, the Platform hasn't yet persisted the Update to the Workflow Execution's Event History or sent it to a Worker.
- Validation. An optional developer-provided function that performs request validation. This validation code, similar to a Query handler, can observe but not change the Workflow state. This means that the validation of an Update request may depend on the Workflow state at runtime. To indicate that the Update request doesn't pass validation, the validation code must throw/return a language-appropriate error. In this case, the system rejects the request, doesn't record anything in the Workflow Event History to indicate that the Update ever happened, and the Update processing doesn't proceed to later phases. If the Update completes the validation stage without error, the Platform changes its state to Accepted and a WorkflowExecutionUpdateAcceptedEvent Event is added to Workflow Execution Event History.
- Execution. Accepted Update requests move to the execution phase. In this phase, the Worker delivers the request to the Update handler.
- Completion. The Update handler can return a result or a language-appropriate error/exception to indicate its completion. The Platform sends the Update outcome back to the original invoking entity as an Update response. A WorkflowExecutionUpdateCompletedEvent Event in the Workflow Execution Event History denotes the completion of an Update.
Workflow Updates are currently disabled by default on Temporal Server.
To enable the UpdateWorkflowExecution
API, set the frontend.enableUpdateWorkflowExecution dynamic config value to true
.
For example, to enable Workflow Updates with the Temporal CLI, pass the value when executing the Temporal CLI command:
temporal server start-dev --dynamic-config-value frontend.enableUpdateWorkflowExecution=true
Footnotesβ
-
The Temporal Service usually deduplicates Signals, but does not guarantee deduplication: During shard migration, two Signal Events (and therefore two deliveries to the Workflow Execution) can be recorded for a single Signal because the deduping info is stored only in memory. β©