Skip to main content

Developer's guide - Features

The Features section of the Temporal Developer's guide provides basic implementation guidance on how to use many of the development features available to Workflows and Activities in the Temporal Platform.

WORK IN PROGRESS

This guide is a work in progress. Some sections may be incomplete or missing for some languages. Information may change at any time.

If you can't find what you are looking for in the Developer's guide, it could be in older docs for SDKs.

In this section you can find the following:

Signals

A SignalLink preview icon

is a message sent to a running Workflow Execution.

Signals are defined in your code and handled in your Workflow Definition. Signals can be sent to Workflow Executions from a Temporal Client or from another Workflow Execution.

Define Signal

A Signal has a name and can have arguments.

  • The name, also called a Signal type, is a string.
  • The arguments must be serializable.

Structs should be used to define Signals and carry data, as long as the struct is serializable via the Data Converter. The Receive() method on the Data Converter decodes the data into the Struct within the Workflow. Only public fields are serializable.

MySignal struct {
Message string // serializable
message string // not serializable
}

Handle Signal

Workflows listen for Signals by the Signal's name.

Use the GetSignalChannel() API from the go.temporal.io/sdk/workflow package to get the Signal Channel.

func YourWorkflowDefinition(ctx workflow.Context, param YourWorkflowParam) error {
// ...
var signal MySignal
signalChan := workflow.GetSignalChannel(ctx, "your-signal-name")
signalChan.Receive(ctx, &signal)
if len(signal.Message) > 0 && signal.Message != "SOME_VALUE" {
return errors.New("signal")
}
// ...
}

In the example above, the Workflow code uses workflow.GetSignalChannel to open a workflow.Channel for the Signal type (identified by the Signal name).

Before completing the Workflow or using Continue-As-New, make sure to do an asynchronous drain on the Signal channel. Otherwise, the Signals will be lost.

Send Signal from Client

When a Signal is sent successfully from the Temporal Client, the WorkflowExecutionSignaled Event appears in the Event History of the Workflow that receives the Signal.

Use the SignalWorkflow() method on an instance of the Go SDK Temporal Client to send a SignalLink preview icon

to a Workflow ExecutionLink preview icon.

Pass in both the Workflow IdLink preview icon

and Run IdLink preview icon to uniquely identify the Workflow Execution. If only the Workflow Id is supplied (provide an empty string as the Run Id param), the Workflow Execution that is Running receives the Signal.

// ...
signal := MySignal {
Message: "Some important data",
}
err = temporalClient.SignalWorkflow(context.Background(), "your-workflow-id", runID, "your-signal-name", signal)
if err != nil {
log.Fatalln("Error sending the Signal", err)
return
}
// ...

Possible errors:

  • serviceerror.NotFound
  • serviceerror.Internal
  • serviceerror.Unavailable

Send Signal from Workflow

A Workflow can send a Signal to another Workflow, in which case it's called an External Signal.

When an External Signal is sent:

A Signal can be sent from within a Workflow to a different Workflow Execution using the SignalExternalWorkflow API from the go.temporal.io/sdk/workflow package.

// ...
func YourWorkflowDefinition(ctx workflow.Context, param YourWorkflowParam) error {
//...
signal := MySignal {
Message: "Some important data",
}
err := workflow.SignalExternalWorkflow(ctx, "some-workflow-id", "", "your-signal-name", signal).Get(ctx, nil)
if err != nil {
// ...
}
// ...
}

Signal-With-Start

Signal-With-Start is used from the Client. It takes a Workflow Id, Workflow arguments, a Signal name, and Signal arguments.

If there's a Workflow running with the given Workflow Id, it will be signaled. If there isn't, a new Workflow will be started and immediately signaled.

Use the SignalWithStartWorkflow() API on the Go SDK Temporal Client to start a Workflow Execution (if not already running) and pass it the Signal at the same time.

Because the Workflow Execution might not exist, this API does not take a Run ID as a parameter

// ...
signal := MySignal {
Message: "Some important data",
}
err = temporalClient.SignalWithStartWorkflow(context.Background(), "your-workflow-id", "your-signal-name", signal)
if err != nil {
log.Fatalln("Error sending the Signal", err)
return
}

Queries

A QueryLink preview icon

is a synchronous operation that is used to get the state of a Workflow Execution.

Define Query

A Query has a name and can have arguments.

  • The name, also called a Query type, is a string.
  • The arguments must be serializable.

In Go, a Query type, also called a Query name, is a string value.

queryType := "your_query_name"

Handle Query

Queries are handled by your Workflow.

Don’t include any logic that causes CommandLink preview icon

generation within a Query handler (such as executing Activities). Including such logic causes unexpected behavior.

Use the SetQueryHandler API from the go.temporal.io/sdk/workflow package to set a Query Handler that listens for a Query by name.

The handler must be a function that returns two values:

  1. A serializable result
  2. An error

The handler function can receive any number of input parameters, but all input parameters must be serializable. The following sample code sets up a Query Handler that handles the current_state Query type:

func YourWorkflow(ctx workflow.Context, input string) error {
currentState := "started" // This could be any serializable struct.
queryType := "current_state"
err := workflow.SetQueryHandler(ctx, queryType, func() (string, error) {
return currentState, nil
})
if err != nil {
currentState = "failed to register query handler"
return err
}
// Your normal Workflow code begins here, and you update the currentState as the code makes progress.
currentState = "waiting timer"
err = NewTimer(ctx, time.Hour).Get(ctx, nil)
if err != nil {
currentState = "timer failed"
return err
}
currentState = "waiting activity"
ctx = WithActivityOptions(ctx, yourActivityOptions)
err = ExecuteActivity(ctx, YourActivity, "your_input").Get(ctx, nil)
if err != nil {
currentState = "activity failed"
return err
}
currentState = "done"
return nil
}

For example, suppose your query handler function takes two parameters:

err := workflow.SetQueryHandler(ctx, "current_state", func(prefix string, suffix string) (string, error) {
return prefix + currentState + suffix, nil
})

Send Query

Queries are sent from a Temporal Client.

Use the QueryWorkflow() API or the QueryWorkflowWithOptions API on the Temporal Client to send a Query to a Workflow Execution.

// ...
response, err := temporalClient.QueryWorkflow(context.Background(), workflowID, runID, queryType)
if err != nil {
// ...
}
// ...

You can pass an arbitrary number of arguments to the QueryWorkflow() function.

// ...
response, err := temporalClient.QueryWorkflow(context.Background(), workflowID, runID, queryType, "foo", "baz")
if err != nil {
// ...
}
// ...

The QueryWorkflowWithOptions() API provides similar functionality, but with the ability to set additional configurations through QueryWorkflowWithOptionsRequest. When using this API, you will also receive a structured response of type QueryWorkflowWithOptionsResponse.

// ...
response, err := temporalClient.QueryWorkflowWithOptions(context.Background(), &client.QueryWorkflowWithOptionsRequest{
WorkflowID: workflowID,
RunID: runID,
QueryType: queryType,
Args: args,
})
if err != nil {
// ...
}

Workflow timeouts

Each Workflow timeout controls the maximum duration of a different aspect of a Workflow Execution.

Workflow timeouts are set when starting the Workflow ExecutionLink preview icon

.

Create an instance of StartWorkflowOptions from the go.temporal.io/sdk/client package, set a timeout, and pass the instance to the ExecuteWorkflow call.

Available timeouts are:

  • WorkflowExecutionTimeout
  • WorkflowRunTimeout
  • WorkflowTaskTimeout
workflowOptions := client.StartWorkflowOptions{
// ...
// Set Workflow Timeout duration
WorkflowExecutionTimeout: time.Hours * 24 * 365 * 10,
// WorkflowRunTimeout: time.Hours * 24 * 365 * 10,
// WorkflowTaskTimeout: time.Second * 10,
// ...
}
workflowRun, err := c.ExecuteWorkflow(context.Background(), workflowOptions, YourWorkflowDefinition)
if err != nil {
// ...
}

Workflow retries

A Retry Policy can work in cooperation with the timeouts to provide fine controls to optimize the execution experience.

Use a Retry PolicyLink preview icon

to retry a Workflow Execution in the event of a failure.

Workflow Executions do not retry by default, and Retry Policies should be used with Workflow Executions only in certain situations.

Create an instance of a RetryPolicy from the go.temporal.io/sdk/temporal package and provide it as the value to the RetryPolicy field of the instance of StartWorkflowOptions.

retrypolicy := &temporal.RetryPolicy{
InitialInterval: time.Second,
BackoffCoefficient: 2.0,
MaximumInterval: time.Second * 100,
}
workflowOptions := client.StartWorkflowOptions{
RetryPolicy: retrypolicy,
// ...
}
workflowRun, err := temporalClient.ExecuteWorkflow(context.Background(), workflowOptions, YourWorkflowDefinition)
if err != nil {
// ...
}

Activity timeouts

Each Activity timeout controls the maximum duration of a different aspect of an Activity Execution.

The following timeouts are available in the Activity Options.

An Activity Execution must have either the Start-To-Close or the Schedule-To-Close Timeout set.

To set an Activity Timeout in Go, create an instance of ActivityOptions from the go.temporal.io/sdk/workflow package, set the Activity Timeout field, and then use the WithActivityOptions() API to apply the options to the instance of workflow.Context.

Available timeouts are:

  • StartToCloseTimeout
  • ScheduleToClose
  • ScheduleToStartTimeout
activityoptions := workflow.ActivityOptions{
// Set Activity Timeout duration
ScheduleToCloseTimeout: 10 * time.Second,
// StartToCloseTimeout: 10 * time.Second,
// ScheduleToStartTimeout: 10 * time.Second,
}
ctx = workflow.WithActivityOptions(ctx, activityoptions)
var yourActivityResult YourActivityResult
err = workflow.ExecuteActivity(ctx, YourActivityDefinition, yourActivityParam).Get(ctx, &yourActivityResult)
if err != nil {
// ...
}

Activity retries

A Retry Policy works in cooperation with the timeouts to provide fine controls to optimize the execution experience.

Activity Executions are automatically associated with a default Retry PolicyLink preview icon

if a custom one is not provided.

To set a RetryPolicyLink preview icon

, create an instance of ActivityOptions from the go.temporal.io/sdk/workflow package, set the RetryPolicy field, and then use the WithActivityOptions() API to apply the options to the instance of workflow.Context.

retrypolicy := &temporal.RetryPolicy{
InitialInterval: time.Second,
BackoffCoefficient: 2.0,
MaximumInterval: time.Second * 100, // 100 * InitialInterval
MaximumAttempts: 0, // Unlimited
NonRetryableErrorTypes: []string, // empty
}

Providing a Retry Policy here is a customization, and overwrites individual Field defaults.

retrypolicy := &temporal.RetryPolicy{
InitialInterval: time.Second,
BackoffCoefficient: 2.0,
MaximumInterval: time.Second * 100,
}

activityoptions := workflow.ActivityOptions{
RetryPolicy: retrypolicy,
}
ctx = workflow.WithActivityOptions(ctx, activityoptions)
var yourActivityResult YourActivityResult
err = workflow.ExecuteActivity(ctx, YourActivityDefinition, yourActivityParam).Get(ctx, &yourActivityResult)
if err != nil {
// ...
}

Activity retry simulator

Use this tool to visualize total Activity Execution times and experiment with different Activity timeouts and Retry Policies.

The simulator is based on a common Activity use-case, which is to call a third party HTTP API and return the results. See the example code snippets below.

Use the Activity Retries settings to configure how long the API request takes to succeed or fail. There is an option to generate scenarios. The Task Time in Queue simulates the time the Activity Task might be waiting in the Task Queue.

Use the Activity Timeouts and Retry Policy settings to see how they impact the success or failure of an Activity Execution.

Sample Activity

import axios from 'axios';

async function testActivity(url: string): Promise<void> {
await axios.get(url);
}

export default testActivity;

Activity Retries (in ms)

×

Activity Timeouts (in ms)

Retry Policy (in ms)

Success after 1 ms

{
"startToCloseTimeout": 10000,
"retryPolicy": {
"backoffCoefficient": 2,
"initialInterval": 1000
}
}

Activity Heartbeats

An Activity HeartbeatLink preview icon

is a ping from the Worker ProcessLink preview icon that is executing the Activity to the Temporal ClusterLink preview icon. Each Heartbeat informs the Temporal Cluster that the Activity ExecutionLink preview icon is making progress and the Worker has not crashed. If the Cluster does not receive a Heartbeat within a Heartbeat TimeoutLink preview icon time period, the Activity will be considered failed and another Activity Task ExecutionLink preview icon may be scheduled according to the Retry Policy.

Heartbeats may not always be sent to the Cluster—they may be throttledLink preview icon

by the Worker.

Activity Cancellations are delivered to Activities from the Cluster when they Heartbeat. Activities that don't Heartbeat can't receive a Cancellation. Heartbeat throttling may lead to Cancellation getting delivered later than expected.

Heartbeats can contain a details field describing the Activity's current progress. If an Activity gets retried, the Activity can access the details from the last Heartbeat that was sent to the Cluster.

To HeartbeatLink preview icon

in an Activity in Go, use the RecordHeartbeat API.

import (
// ...
"go.temporal.io/sdk/workflow"
// ...
)

func YourActivityDefinition(ctx, YourActivityDefinitionParam) (YourActivityDefinitionResult, error) {
// ...
activity.RecordHeartbeat(ctx, details)
// ...
}

When an Activity Task Execution times out due to a missed Heartbeat, the last value of the details variable above is returned to the calling Workflow in the details field of TimeoutError with TimeoutType set to Heartbeat.

You can also Heartbeat an Activity from an external source:

// The client is a heavyweight object that should be created once per process.
temporalClient, err := client.Dial(client.Options{})
// Record heartbeat.
err := temporalClient.RecordActivityHeartbeat(ctx, taskToken, details)

The parameters of the RecordActivityHeartbeat function are:

  • taskToken: The value of the binary TaskToken field of the ActivityInfo struct retrieved inside the Activity.
  • details: The serializable payload containing progress information.

If an Activity Execution Heartbeats its progress before it failed, the retry attempt will have access to the progress information, so that the Activity Execution can resume from the failed state. Here's an example of how this can be implemented:

func SampleActivity(ctx context.Context, inputArg InputParams) error {
startIdx := inputArg.StartIndex
if activity.HasHeartbeatDetails(ctx) {
// Recover from finished progress.
var finishedIndex int
if err := activity.GetHeartbeatDetails(ctx, &finishedIndex); err == nil {
startIdx = finishedIndex + 1 // Start from next one.
}
}

// Normal Activity logic...
for i:=startIdx; i<inputArg.EndIdx; i++ {
// Code for processing item i goes here...
activity.RecordHeartbeat(ctx, i) // Report progress.
}
}

Heartbeat Timeout

A Heartbeat TimeoutLink preview icon

works in conjunction with Activity HeartbeatsLink preview icon.

To set a Heartbeat TimeoutLink preview icon

, Create an instance of ActivityOptions from the go.temporal.io/sdk/workflow package, set the RetryPolicy field, and then use the WithActivityOptions() API to apply the options to the instance of workflow.Context.

activityoptions := workflow.ActivityOptions{
HeartbeatTimeout: 10 * time.Second,
}
ctx = workflow.WithActivityOptions(ctx, activityoptions)
var yourActivityResult YourActivityResult
err = workflow.ExecuteActivity(ctx, YourActivityDefinition, yourActivityParam).Get(ctx, &yourActivityResult)
if err != nil {
// ...
}

Asynchronous Activity Completion

Asynchronous Activity CompletionLink preview icon enables the Activity Function to return without the Activity Execution completing.

There are three steps to follow:

  1. The Activity provides the external system with identifying information needed to complete the Activity Execution. Identifying information can be a Task TokenLink preview icon, or a combination of Namespace, Workflow Id, and Activity Id.
  2. The Activity Function completes in a way that identifies it as waiting to be completed by an external system.
  3. The Temporal Client is used to Heartbeat and complete the Activity.
  1. Provide the external system with the a Task Token to complete the Activity Execution. To do this, use the GetInfo() API from the go.temporal.io/sdk/activity package.
// Retrieve the Activity information needed to asynchronously complete the Activity.
activityInfo := activity.GetInfo(ctx)
taskToken := activityInfo.TaskToken
// Send the taskToken to the external service that will complete the Activity.
  1. Return an activity.ErrResultPending error to indicate that the Activity is completing asynchronously.
return "", activity.ErrResultPending
  1. Use the Temporal Client to complete the Activity using the Task Token.
// Instantiate a Temporal service client.
// The same client can be used to complete or fail any number of Activities.
// The client is a heavyweight object that should be created once per process.
temporalClient, err := client.Dial(client.Options{})

// Complete the Activity.
temporalClient.CompleteActivity(context.Background(), taskToken, result, nil)

The following are the parameters of the CompleteActivity function:

  • taskToken: The value of the binary TaskToken field of the ActivityInfo struct retrieved inside the Activity.
  • result: The return value to record for the Activity. The type of this value must match the type of the return value declared by the Activity function.
  • err: The error code to return if the Activity terminates with an error.

If error is not null, the value of the result field is ignored.

To fail the Activity, you would do the following:

// Fail the Activity.
client.CompleteActivity(context.Background(), taskToken, nil, err)

Child Workflows

A Child Workflow ExecutionLink preview icon

is a Workflow Execution that is scheduled from within another Workflow using a Child Workflow API.

When using a Child Workflow API, Child Workflow related Events (StartChildWorkflowExecutionInitiated, ChildWorkflowExecutionStarted, ChildWorkflowExecutionCompleted, etc...) are logged in the Workflow Execution Event History.

Always block progress until the ChildWorkflowExecutionStarted Event is logged to the Event History to ensure the Child Workflow Execution has started. After that, Child Workflow Executions may be abandoned using the default Abandon Parent Close PolicyLink preview icon

set in the Child Workflow Options.

To be sure that the Child Workflow Execution has started, first call the Child Workflow Execution method on the instance of Child Workflow future, which returns a different future.

Then get the value of an object that acts as a proxy for a result that is initially unknown, which is what waits until the Child Workflow Execution has spawned.

To spawn a Child Workflow ExecutionLink preview icon

in Go, use the ExecuteChildWorkflow API, which is available from the go.temporal.io/sdk/workflow package.

The ExecuteChildWorkflow call requires an instance of workflow.Context, with an instance of workflow.ChildWorkflowOptions applied to it, the Workflow Type, and any parameters that should be passed to the Child Workflow Execution.

workflow.ChildWorkflowOptions contain the same fields as client.StartWorkflowOptions. Workflow Option fields automatically inherit their values from the Parent Workflow Options if they are not explicitly set. If a custom WorkflowID is not set, one is generated when the Child Workflow Execution is spawned. Use the WithChildOptions API to apply Child Workflow Options to the instance of workflow.Context.

The ExecuteChildWorkflow call returns an instance of a ChildWorkflowFuture.

Call the .Get() method on the instance of ChildWorkflowFuture to wait for the result.

func YourWorkflowDefinition(ctx workflow.Context, params ParentParams) (ParentResp, error) {

childWorkflowOptions := workflow.ChildWorkflowOptions{}
ctx = workflow.WithChildOptions(ctx, childWorkflowOptions)

var result ChildResp
err := workflow.ExecuteChildWorkflow(ctx, YourOtherWorkflowDefinition, ChildParams{}).Get(ctx, &result)
if err != nil {
// ...
}
// ...
return resp, nil
}

func YourOtherWorkflowDefinition(ctx workflow.Context, params ChildParams) (ChildResp, error) {
// ...
return resp, nil
}

To asynchronously spawn a Child Workflow Execution, the Child Workflow must have an "Abandon" Parent Close Policy set in the Child Workflow Options. Additionally, the Parent Workflow Execution must wait for the ChildWorkflowExecutionStarted Event to appear in its Event History before it completes.

If the Parent makes the ExecuteChildWorkflow call and then immediately completes, the Child Workflow Execution does not spawn.

To be sure that the Child Workflow Execution has started, first call the GetChildWorkflowExecution method on the instance of the ChildWorkflowFuture, which will return a different Future. Then call the Get() method on that Future, which is what will wait until the Child Workflow Execution has spawned.

import (
// ...
"go.temporal.io/api/enums/v1"
)

func YourWorkflowDefinition(ctx workflow.Context, params ParentParams) (ParentResp, error) {

childWorkflowOptions := workflow.ChildWorkflowOptions{
ParentClosePolicy: enums.PARENT_CLOSE_POLICY_ABANDON,
}
ctx = workflow.WithChildOptions(ctx, childWorkflowOptions)

childWorkflowFuture := workflow.ExecuteChildWorkflow(ctx, YourOtherWorkflowDefinition, ChildParams{})
// Wait for the Child Workflow Execution to spawn
var childWE workflow.Execution
if err := childWorkflowFuture.GetChildWorkflowExecution().Get(ctx, &childWE); err != nil {
return err
}
// ...
return resp, nil
}

func YourOtherWorkflowDefinition(ctx workflow.Context, params ChildParams) (ChildResp, error) {
// ...
return resp, nil
}

Parent Close Policy

A Parent Close PolicyLink preview icon

determines what happens to a Child Workflow Execution if its Parent changes to a Closed status (Completed, Failed, or Timed Out).

In Go, a Parent Close Policy is set on the ParentClosePolicy field of an instance of workflow.ChildWorkflowOptions. The possible values can be obtained from the go.temporal.io/api/enums/v1 package.

  • PARENT_CLOSE_POLICY_ABANDON
  • PARENT_CLOSE_POLICY_TERMINATE
  • PARENT_CLOSE_POLICY_REQUEST_CANCEL

The Child Workflow Options are then applied to the instance of workflow.Context by using the WithChildOptions API, which is then passed to the ExecuteChildWorkflow() call.

import (
// ...
"go.temporal.io/api/enums/v1"
)

func YourWorkflowDefinition(ctx workflow.Context, params ParentParams) (ParentResp, error) {
// ...
childWorkflowOptions := workflow.ChildWorkflowOptions{
// ...
ParentClosePolicy: enums.PARENT_CLOSE_POLICY_ABANDON,
}
ctx = workflow.WithChildOptions(ctx, childWorkflowOptions)
childWorkflowFuture := workflow.ExecuteChildWorkflow(ctx, YourOtherWorkflowDefinition, ChildParams{})
// ...
}

func YourOtherWorkflowDefinition(ctx workflow.Context, params ChildParams) (ChildResp, error) {
// ...
return resp, nil
}

Continue-As-New

Continue-As-NewLink preview icon enables a Workflow Execution to close successfully and create a new Workflow Execution in a single atomic operation if the number of Events in the Event History is becoming too large. The Workflow Execution spawned from the use of Continue-As-New has the same Workflow Id, a new Run Id, and a fresh Event History and is passed all the appropriate parameters.

To cause a Workflow Execution to Continue-As-NewLink preview icon

, the Workflow function should return the result of the NewContinueAsNewError() API available from the go.temporal.io/sdk/workflow package.

func SimpleWorkflow(ctx workflow.Context, value string) error {
...
return workflow.NewContinueAsNewError(ctx, SimpleWorkflow, value)
}

To check whether a Workflow Execution was spawned as a result of Continue-As-New, you can check if workflow.GetInfo(ctx).ContinuedExecutionRunID is not empty (i.e. "").

Notes

  • To prevent Signal loss, be sure to perform an asynchronous drain on the Signal channel. Failure to do so can result in buffered Signals being ignored and lost.
  • Make sure that the previous Workflow and the Continue-As-New Workflow are referenced by the same alias. Failure to do so can cause the Workflow to Continue-As-New on an entirely different Workflow.

Timers

A Workflow can set a durable timer for a fixed time period. In some SDKs, the function is called sleep(), and in others, it's called timer().

A Workflow can sleep for months. Timers are persisted, so even if your Worker or Temporal Cluster is down when the time period completes, as soon as your Worker and Cluster are back up, the sleep() call will resolve and your code will continue executing.

Sleeping is a resource-light operation: it does not tie up the process, and you can run millions of Timers off a single Worker.

To set a Timer in Go, use the NewTimer() function and pass the duration you want to wait before continuing.

timer := workflow.NewTimer(timerCtx, duration)

To set a sleep duration in Go, use the sleep() function and pass the duration you want to wait before continuing. A zero or negative sleep duration causes the function to return immediately.

sleep = workflow.Sleep(ctx, 10*time.Second)

For more information, see the Timer example in the Go Samples repository.

Temporal Cron Jobs

A Temporal Cron JobLink preview icon

is the series of Workflow Executions that occur when a Cron Schedule is provided in the call to spawn a Workflow Execution.

A Cron Schedule is provided as an option when the call to spawn a Workflow Execution is made.

Create an instance of StartWorkflowOptions from the go.temporal.io/sdk/client package, set the CronSchedule field, and pass the instance to the ExecuteWorkflow call.

  • Type: string
  • Default: None
workflowOptions := client.StartWorkflowOptions{
CronSchedule: "15 8 * * *",
// ...
}
workflowRun, err := c.ExecuteWorkflow(context.Background(), workflowOptions, YourWorkflowDefinition)
if err != nil {
// ...
}

Side Effects

Side Effects are used to execute nondeterministic code, such as generating a UUID or a random number, without compromising deterministic in the Workflow. This is done by storing the nondeterministic results of the Side Effect into the Workflow Event History.

A Side Effect does not re-execute during a Replay. Instead, it returns the recorded result from the Workflow Execution Event History.

Side Effects should not fail. An exception that is thrown from the Side Effect causes failure and retry of the current Workflow Task.

An Activity or a Local Activity may also be used instead of a Side effect, as its result is also persisted in Workflow Execution History.

note

You shouldn’t modify the Workflow state inside a Side Effect function, because it is not reexecuted during Replay. Side Effect function should be used to return a value.

Use the SideEffect function from the go.temporal.io/sdk/workflow package to execute a Side Effect directly in your Workflow.

Pass it an instance of context.Context and the function to execute.

The SideEffect API returns a Future, an instance of converter.EncodedValue.

Use the Get method on the Future to retrieve the result of the Side Effect.

Correct implementation

The following example demonstrates the correct way to use SideEffect:

encodedRandom := workflow.SideEffect(ctx, func(ctx workflow.Context) interface{} {
return rand.Intn(100)
})

var random int
encodedRandom.Get(&random)
// ...
}

Incorrect implementation

The following example demonstrates how NOT to use SideEffect:

// Warning: This is an incorrect example.
// This code is nondeterministic.
var random int
workflow.SideEffect(func(ctx workflow.Context) interface{} {
random = rand.Intn(100)
return nil
})
// random will always be 0 in replay, so this code is nondeterministic.

On replay the provided function is not executed, the random number will always be 0, and the Workflow Execution could take a different path, breaking determinism.

Mutable Side Effects

Mutable Side Effects execute the provided function once, and then it looks up the History of the value with the given Workflow ID.

  • If there is no existing value, then it records the function result as a value with the given Workflow ID on the History.
  • If there is an existing value, then it compares whether the existing value from the History has changed from the new function results, by calling the equals function.
    • If the values are equal, then it returns the value without recording a new Marker Event
    • If the values aren't equal, then it records the new value with the same ID on the History.
note

During a Workflow Execution, every new Side Effect call results in a new Marker recorded on the Workflow History; whereas Mutable Side Effects only records a new Marker on the Workflow History if the value for the Side Effect ID changes or is set the first time.

During a Replay, Mutable Side Effects will not execute the function again. Instead, it returns the exact same value that was returned during the Workflow Execution.

To use MutableSideEffect() in Go, provide a unique name within the scope of the workflow.

if err := workflow.MutableSideEffect(ctx, "configureNumber", get, eq).Get(&number); err != nil {
panic("can't decode number:" + err.Error())
}

Environment variables

Environment variables can be provided in the normal way for our language to our Client, Worker, and Activity code. They can't be used normally with Workflow code, as that would be nondeterministic (if the environment variables changed between Workflow replays, the code that used them would behave differently).

Most of the time, you can provide environment variables in your Activity function; however, if you need them in your Workflow functions, you can use the following options:

  • Provide environment variables as arguments when starting the Workflow.
  • Call a Local Activity at the beginning of the Workflow that returns environment variables.

In either case, the environment variables will appear in Event History, so you may want to use an encryption Data Converter.

Content is currently unavailable.

Namespaces

A NamespaceLink preview icon

is a unit of isolation within the Temporal Platform.

You can use Namespaces to match the development lifecycle; for example, having separate dev and prod Namespaces. Or you could use them to ensure Workflow Executions between different teams never communicate; such as ensuring that the teamA Namespace never impacts the teamB Namespace.

On Temporal Cloud, use the Temporal Cloud UILink preview icon

or tcld commands to create and manage Namespaces.

On self-hosted Temporal Cluster, you can register and manage your Namespaces using tctl (recommended) or programmatically using APIs. Note that these APIs and tctl commands will not work with Temporal Cloud.

Use a custom AuthorizerLink preview icon

on your Frontend Service in the Temporal Cluster to set restrictions on who can create, update, or deprecate Namespaces.

You must register a Namespace with the Temporal Cluster before setting it in the Temporal Client.

Use Register API with the NamespaceClient interface to register a NamespaceLink preview icon

and set the Retention PeriodLink preview icon for the Workflow Execution Event History for the Namespace.

You can also register Namespaces using the tctl command-line toolLink preview icon

.

client, err := client.NewNamespaceClient(client.Options{HostPort: ts.config.ServiceAddr})
//...
err = client.Register(ctx, &workflowservice.RegisterNamespaceRequest{
Namespace: your-namespace-name,
WorkflowExecutionRetentionPeriod: &retention,
})

The Retention Period setting using WorkflowExecutionRetentionPeriod is mandatory. The minimum value you can set for this period is 1 day.

Once registered, set Namespace using Dial in a Workflow Client to run your Workflow Executions within that Namespace. See how to set Namespace in a Client in GoLink preview icon

for details.

Note that Namespace registration using this API takes up to 10 seconds to complete. Ensure that you wait for this registration to complete before starting the Workflow Execution against the Namespace.

To update your Namespace, use the Update API with the NamespaceClient.

To update your Namespace using tctl, use the tctl namespace updateLink preview icon

command.