Go SDK developer's guide - Foundations
The Foundations section of the Temporal Developer's guide covers the minimum set of concepts and implementation details needed to build and run a Temporal ApplicationWhat is a Temporal Application
A Temporal Application is a set of Workflow Executions.
Learn more—that is, all the relevant steps to start a Workflow Execution that executes an Activity.
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:
- Run a development ClusterHow to install Temporal CLI and run a development server
undefined
Learn more - Install your SDKHow to install a Temporal SDK
A Temporal SDK provides a framework for Temporal Application development.
Learn more - Connect to a dev ClusterHow to connect a Temporal Client to a Temporal Cluster
When connecting a Temporal Client to a Temporal Cluster, you must provide the address and port number of the Temporal Cluster.
Learn more - Connect to Temporal CloudHow to connect to Temporal Cloud
Use a compatible mTLS CA certificate and mTLS private key and your Cloud Namespace to connect to Temporal Cloud.
Learn more - Develop a WorkflowHow to develop a basic Workflow
Workflows are the fundamental unit of a Temporal Application, and it all starts with the development of a Workflow Definition.
Learn more - Develop an ActivityHow to develop an Activity Definition in Go
In the Temporal Go SDK programming model, an Activity Definition is an exportable function or a `struct` method.
Learn more - Start an Activity ExecutionHow to start an Activity Execution
Calls to spawn Activity Executions are written within a Workflow Definition.
Learn more - Run a dev WorkerHow to develop a Worker in Go
Develop an instance of a Worker by calling worker.New(), available via the go.temporal.io/sdk/worker package.
Learn more - Run a Temporal Cloud WorkerHow to run a Temporal Cloud Worker
The Worker Process is where Workflow Functions and Activity Functions are executed.
Learn more - Start a Workflow ExecutionHow to start a Workflow Execution
Workflow Execution semantics rely on several parameters—that is, to start a Workflow Execution you must supply a Task Queue that will be used for the Tasks (one that a Worker is polling), the Workflow Type, language-specific contextual data, and Workflow Function parameters.
Learn more
Run a development server
This section describes how to install the Temporal CLI and run a development Cluster. The local development Cluster comes packaged with the Temporal Web UI.
For information on deploying and running a production Cluster, see the Cluster deployment guide, or sign up for Temporal Cloud and let us run your production Cluster for you.
Temporal CLI is a tool for interacting with a Temporal Cluster from the command line and it includes a distribution of the Temporal Server and Web UI. This local development Cluster runs as a single process with zero runtime dependencies and it supports persistence to disk and in-memory mode through SQLite.
Install the Temporal CLI
Choose one of the following install methods to install the Temporal CLI.
- macOS
- Linux
- Windows
Install the Temporal CLI with Homebrew.
brew install temporal
Install the Temporal CLI with cURL.
curl -sSf https://temporal.download/cli.sh | sh
Install the Temporal CLI from CDN.
- Select the platform and architecture needed.
- Extract the downloaded archive.
- Add the
temporal
binary to your PATH.
Install the Temporal CLI with cURL.
curl -sSf https://temporal.download/cli.sh | sh
Install the Temporal CLI from CDN.
- Select the platform and architecture needed.
- Extract the downloaded archive.
- Add the
temporal
binary to your PATH.
- Install the Temporal CLI from CDN.
- Select the platform and architecture needed and download the binary.
- Extract the downloaded archive.
- Add the
temporal.exe
binary to your PATH.
Start the Temporal Development Server
Start the Temporal Development Server by using the server start-dev
command.
temporal server start-dev
This command automatically starts the Web UI, creates the default Namespace, and uses an in-memory database.
The Temporal Server should be available on localhost:7233
and the Temporal Web UI should be accessible at http://localhost:8233
.
The server's startup configuration can be customized using command line options. For a full list of options, run:
temporal server start-dev --help
Install a Temporal SDK
A Temporal SDKWhat is a Temporal SDK?
A Temporal SDK is a language-specific library that offers APIs to construct and use a Temporal Client to communicate with a Temporal Cluster, develop Workflow Definitions, and develop Worker Programs.
Learn more provides a framework for Temporal ApplicationWhat is a Temporal Application
A Temporal Application is a set of Workflow Executions.
Learn more development.
An SDK provides you with the following:
- A Temporal ClientWhat is a Temporal Client
A Temporal Client, provided by a Temporal SDK, provides a set of APIs to communicate with a Temporal Cluster.
Learn more to communicate with a Temporal ClusterWhat is a Temporal Cluster?
A Temporal Cluster is the Temporal Server paired with persistence.
Learn more. - APIs to develop WorkflowsWhat is a Workflow?
In day-to-day conversations, the term "Workflow" frequently denotes either a Workflow Type, a Workflow Definition, or a Workflow Execution.
Learn more. - APIs to create and manage Worker ProcessesWhat is a Worker?
In day-to-day conversations, the term Worker is used to denote both a Worker Program and a Worker Process. Temporal documentation aims to be explicit and differentiate between them.
Learn more. - APIs to author ActivitiesWhat is an Activity Definition?
An Activity Definition is the code that defines the constraints of an Activity Task Execution.
Learn more.
Add the Temporal Go SDK to your project:
go get go.temporal.io/sdk
Or clone the Go SDK repo to your preferred location:
git clone git@github.com:temporalio/sdk-go.git
API reference
The Temporal Go SDK API reference is published on pkg.go.dev.
- Short link:
t.mp/go-api
Code samples
You can find a complete list of executable code samples in Temporal's GitHub repository.
Additionally, several of the Tutorials are backed by a fully executable template application.
- Go Samples repo
- Background Check application: Provides a non-trivial Temporal Application implementation in conjunction with application documentation.
- Hello world application template in Go: Provides a quick-start development app for users. This sample works in conjunction with the "Hello World!" from scratch tutorial in Go.
- Money transfer application template in Go: Provides a quick-start development app for users. It demonstrates a basic "money transfer" Workflow Definition and works in conjunction with the Run your first app tutorial in Go.
- Subscription-style Workflow Definition in Go: Demonstrates some of the patterns that could be implemented for a subscription-style business process.
- eCommerce application example in Go: Showcases a per-user shopping cart–style Workflow Definition that includes an API for adding and removing items from the cart as well as a web UI. This application sample works in conjunction with the eCommerce in Go tutorial.
Connect to a dev Cluster
A Temporal ClientWhat is a Temporal Client
A Temporal Client, provided by a Temporal SDK, provides a set of APIs to communicate with a Temporal Cluster.
Learn more enables you to communicate with the Temporal ClusterWhat is a Temporal Cluster?
A Temporal Cluster is the Temporal Server paired with persistence.
Learn more.
Communication with a Temporal Cluster includes, but isn't limited to, the following:
- Starting Workflow Executions.
- Sending Signals to Workflow Executions.
- Sending Queries to Workflow Executions.
- Getting the results of a Workflow Execution.
- Providing an Activity Task Token.
A Temporal Client cannot be initialized and used inside a Workflow. However, it is acceptable and common to use a Temporal Client inside an Activity to communicate with a Temporal Cluster.
When you are running a Cluster locally (such as Temporalite), the number of connection options you must provide is minimal.
Many SDKs default to the local host or IP address and port that Temporalite and Docker Compose serve (127.0.0.1:7233
).
Use the Dial()
API available in the go.temporal.io/sdk/client
package to create a Client
.
If you don't provide HostPort
, the Client defaults the address and port number to 127.0.0.1:7233
, which is the port of the development Cluster.
If you don't set a custom Namespace name in the Namespace field, the client connects to the default Namespace.
View source codepackage main
import (
"context"
"encoding/json"
"log"
"net/http"
"documentation-samples-go/yourapp"
"go.temporal.io/sdk/client"
)
func main() {
// Create a Temporal Client to communicate with the Temporal Cluster.
// A Temporal Client is a heavyweight object that should be created just once per process.
temporalClient, err := client.Dial(client.Options{
HostPort: client.DefaultHostPort,
})
if err != nil {
log.Fatalln("Unable to create Temporal Client", err)
}
defer temporalClient.Close()
// ...
}
Connect to Temporal Cloud
When you connect to Temporal Cloud, you need to provide additional connection and client options that include the following:
- An address that includes your Cloud Namespace NameWhat is a Namespace?
A Namespace is a unit of isolation within the Temporal Platform
Learn more and a port number:<Namespace>.<ID>.tmprl.cloud:<port>
. - mTLS CA certificate.
- mTLS private key.
For more information about managing and generating client certificates for Temporal Cloud, see How to manage certificates in Temporal Cloud.
For more information about configuring TLS to secure inter- and intra-network communication for a Temporal Cluster, see Temporal Customization Samples.
To connect to and run Workflows through Temporal Cloud, you need the following:
- A compatible mTLS CA certificate and mTLS private key that has been added to your Namespace.
See certificate requirementsRequirements for CA certificates in Temporal Cloud
Certificates provided to Temporal for your Namespaces must meet certain requirements.
Learn more. - Your Temporal Cloud Namespace IdWhat is a Cloud Namespace Id?
A Cloud Namespace Id is a globally unique identifier for a Namespace in Temporal Cloud.
Learn more, which includes your Temporal Cloud Namespace NameWhat is a Cloud Namespace Name?
A Cloud Namespace Name is a customer-supplied name for a Namespace in Temporal Cloud.
Learn more and the unique five- or six-digit Temporal Cloud Account IdWhat is a Temporal Cloud Account Id?
A Temporal Cloud Account Id is a unique identifier for a customer.
Learn more that is appended to it. This information can be found in the URL of your Namespace; for example,https://cloud.temporal.io/namespaces/yournamespace.a2fx6/
. Remember that the Namespace Id must include the Account Id:yournamespace.a2fx6
.
For more information about managing and generating client certificates for Temporal Cloud, see How to manage certificates in Temporal Cloud.
For more information about configuring TLS to secure inter- and intra-network communication for a Temporal Cluster, see Temporal Customization Samples.
View source codepackage main
import (
"context"
"crypto/tls"
"encoding/json"
"log"
"documentation-samples-go/cloud"
"go.temporal.io/sdk/client"
)
func main() {
// Get the key and cert from your env or local machine
clientKeyPath := "./secrets/yourkey.key"
clientCertPath := "./secrets/yourcert.pem"
// Specify the host and port of your Temporal Cloud Namespace
// Host and port format: namespace.unique_id.tmprl.cloud:port
hostPort := "<yournamespace>.<id>.tmprl.cloud:7233"
namespace := "<yournamespace>.<id>"
// Use the crypto/tls package to create a cert object
cert, err := tls.LoadX509KeyPair(clientCertPath, clientKeyPath)
if err != nil {
log.Fatalln("Unable to load cert and key pair.", err)
}
// Add the cert to the tls certificates in the ConnectionOptions of the Client
temporalClient, err := client.Dial(client.Options{
HostPort: hostPort,
Namespace: namespace,
ConnectionOptions: client.ConnectionOptions{
TLS: &tls.Config{Certificates: []tls.Certificate{cert}},
},
})
if err != nil {
log.Fatalln("Unable to connect to Temporal Cloud.", err)
}
defer temporalClient.Close()
// ...
}
Develop Workflows
Workflows are the fundamental unit of a Temporal Application, and it all starts with the development of a Workflow DefinitionWhat is a Workflow Definition?
A Workflow Definition is the code that defines the constraints of a Workflow Execution.
Learn more.
In the Temporal Go SDK programming model, a Workflow DefinitionWhat is a Workflow Definition?
A Workflow Definition is the code that defines the constraints of a Workflow Execution.
Learn more is an exportable function.
Below is an example of a basic Workflow Definition.
package yourapp
import (
"time"
"go.temporal.io/sdk/workflow"
)
// ...
// YourSimpleWorkflowDefintiion is the most basic Workflow Defintion.
func YourSimpleWorkflowDefinition(ctx workflow.Context) error {
// ...
return nil
}
Workflow parameters
Temporal Workflows may have any number of custom parameters. However, we strongly recommend that objects are used as parameters, so that the object's individual fields may be altered without breaking the signature of the Workflow. All Workflow Definition parameters must be serializable.
The first parameter of a Go-based Workflow Definition must be of the workflow.Context
type.
It is used by the Temporal Go SDK to pass around Workflow Execution context, and virtually all the Go SDK APIs that are callable from the Workflow require it.
It is acquired from the go.temporal.io/sdk/workflow
package.
The workflow.Context
entity operates similarly to the standard context.Context
entity provided by Go.
The only difference between workflow.Context
and context.Context
is that the Done()
function, provided by workflow.Context
, returns workflow.Channel
instead of the standard Go chan
.
Additional parameters can be passed to the Workflow when it is invoked.
A Workflow Definition may support multiple custom parameters, or none.
These parameters can be regular type variables or safe pointers.
However, the best practice is to pass a single parameter that is of a struct
type, so there can be some backward compatibility if new parameters are added.
All Workflow Definition parameters must be serializable and can't be channels, functions, variadic, or unsafe pointers.
View source codepackage yourapp
import (
"time"
"go.temporal.io/sdk/workflow"
)
// YourWorkflowParam is the object passed to the Workflow.
type YourWorkflowParam struct {
WorkflowParamX string
WorkflowParamY int
}
// ...
// YourWorkflowDefinition is your custom Workflow Definition.
func YourWorkflowDefinition(ctx workflow.Context, param YourWorkflowParam) (*YourWorkflowResultObject, error) {
// ...
}
Workflow return values
Workflow return values must also be serializable. Returning results, returning errors, or throwing exceptions is fairly idiomatic in each language that is supported. However, Temporal APIs that must be used to get the result of a Workflow Execution will only ever receive one of either the result or the error.
A Go-based Workflow Definition can return either just an error
or a customValue, error
combination.
Again, the best practice here is to use a struct
type to hold all custom values.
A Workflow Definition written in Go can return both a custom value and an error.
However, it's not possible to receive both a custom value and an error in the calling process, as is normal in Go.
The caller will receive either one or the other.
Returning a non-nil error
from a Workflow indicates that an error was encountered during its execution and the Workflow Execution should be terminated, and any custom return values will be ignored by the system.
package yourapp
import (
"time"
"go.temporal.io/sdk/workflow"
)
// ...
// YourWorkflowResultObject is the object returned by the Workflow.
type YourWorkflowResultObject struct {
WFResultFieldX string
WFResultFieldY int
}
// ...
// YourWorkflowDefinition is your custom Workflow Definition.
func YourWorkflowDefinition(ctx workflow.Context, param YourWorkflowParam) (*YourWorkflowResultObject, error) {
// ...
if err != nil {
return nil, err
}
// Make the results of the Workflow Execution available.
workflowResult := &YourWorkflowResultObject{
WFResultFieldX: activityResult.ResultFieldX,
WFResultFieldY: activityResult.ResultFieldY,
}
return workflowResult, nil
}
Workflow Type
Workflows have a Type that are referred to as the Workflow name.
The following examples demonstrate how to set a custom name for your Workflow Type.
In Go, by default, the Workflow Type name is the same as the function name.
To customize the Workflow Type, set the Name
parameter with RegisterOptions
when registering your Workflow with a WorkerHow to develop a Worker in Go
Develop an instance of a Worker by calling worker.New(), available via the go.temporal.io/sdk/worker package.
Learn more.
package main
import (
"log"
"go.temporal.io/sdk/activity"
"go.temporal.io/sdk/client"
"go.temporal.io/sdk/worker"
"go.temporal.io/sdk/workflow"
"documentation-samples-go/yourapp"
)
// ...
func main() {
// ...
yourWorker := worker.New(temporalClient, "your-custom-task-queue-name", worker.Options{})
// ...
// Use RegisterOptions to set the name of the Workflow Type for example.
registerWFOptions := workflow.RegisterOptions{
Name: "JustAnotherWorkflow",
}
yourWorker.RegisterWorkflowWithOptions(yourapp.YourSimpleWorkflowDefinition, registerWFOptions)
// ...
}
Workflow logic requirements
Workflow logic is constrained by deterministic execution requirementsWhat is a Workflow Definition?
A Workflow Definition is the code that defines the constraints of a Workflow Execution.
Learn more.
Therefore, each language is limited to the use of certain idiomatic techniques.
However, each Temporal SDK provides a set of APIs that can be used inside your Workflow to interact with external (to the Workflow) application code.
In Go, Workflow Definition code cannot directly do the following:
- Iterate over maps using
range
, because withrange
the order of the map's iteration is randomized. Instead you can collect the keys of the map, sort them, and then iterate over the sorted keys to access the map. This technique provides deterministic results. You can also use a Side Effect or an Activity to process the map instead. - Call an external API, conduct a file I/O operation, talk to another service, etc. (Use an Activity for these.)
The Temporal Go SDK has APIs to handle equivalent Go constructs:
workflow.Now()
This is a replacement fortime.Now()
.workflow.Sleep()
This is a replacement fortime.Sleep()
.workflow.GetLogger()
This ensures that the provided logger does not duplicate logs during a replay.workflow.Go()
This is a replacement for thego
statement.workflow.Channel
This is a replacement for the nativechan
type. Temporal provides support for both buffered and unbuffered channels.workflow.Selector
This is a replacement for theselect
statement. Learn more on the Go SDK Selectors page.workflow.Context
This is a replacement forcontext.Context
. See Tracing for more information about context propagation.
Activity Definition
In the Temporal Go SDK programming model, an Activity Definition is an exportable function or a struct
method.
Below is an example of both a basic Activity Definition and of an Activity defined as a Struct method.
An Activity struct can have more than one method, with each method acting as a separate Activity Type.
Activities written as struct methods can use shared struct variables, such as:
- an application level DB pool
- client connection to another service
- reusable utilities
- any other expensive resources that you only want to initialize once per process
Because this is such a common need, the rest of this guide shows Activities written as struct
methods.
package yourapp
import (
"context"
"go.temporal.io/sdk/activity"
)
// ...
// YourSimpleActivityDefinition is a basic Activity Definiton.
func YourSimpleActivityDefinition(ctx context.Context) error {
return nil
}
// YourActivityObject is the struct that maintains shared state across Activities.
// If the Worker crashes this Activity object loses its state.
type YourActivityObject struct {
Message *string
Number *int
}
// YourActivityDefinition is your custom Activity Definition.
// An Activity Definiton is an exportable function.
func (a *YourActivityObject) YourActivityDefinition(ctx context.Context, param YourActivityParam) (*YourActivityResultObject, error) {
// ...
}
Activity parameters
There is no explicit limit to the total number of parameters that an Activity DefinitionWhat is an Activity Definition?
An Activity Definition is the code that defines the constraints of an Activity Task Execution.
Learn more may support.
However, there is a limit of the total size of the data ends up encoded into a gRPC message Payload.
A single argument is limited to a maximum size of 2 MB. And the total size of a gRPC message, which includes all the arguments, is limited to a maximum of 4 MB.
Also, keep in mind that all Payload data is recorded in the Workflow Execution Event HistoryWhat is an Event History?
An append log of Events that represents the full state a Workflow Execution.
Learn more and large Event Histories can affect Worker performance.
This is because the entire Event History could be transferred to a Worker Process with a Workflow TaskWhat is a Workflow Task?
A Workflow Task is a Task that contains the context needed to make progress with a Workflow Execution.
Learn more.
Some SDKs require that you pass context objects, others do not. When it comes to your application data—that is, data that is serialized and encoded into a Payload—we recommend that you use a single object as an argument that wraps the application data passed to Activities. This is so that you can change what data is passed to the Activity without breaking a function or method signature.
The first parameter of an Activity Definition is context.Context
.
This parameter is optional for an Activity Definition, though it is recommended, especially if the Activity is expected to use other Go SDK APIs.
An Activity Definition can support as many other custom parameters as needed. However, all parameters must be serializable (parameters can’t be channels, functions, variadic, or unsafe pointers), and it is recommended to pass a single struct that can be updated later.
View source code// YourActivityParam is the struct passed to your Activity.
// Use a struct so that your function signature remains compatible if fields change.
type YourActivityParam struct {
ActivityParamX string
ActivityParamY int
}
// ...
func (a *YourActivityObject) YourActivityDefinition(ctx context.Context, param YourActivityParam) (*YourActivityResultObject, error) {
// ...
}
Activity return values
All data returned from an Activity must be serializable.
There is no explicit limit to the amount of data that can be returned by an Activity, but keep in mind that all return values are recorded in a Workflow Execution Event HistoryWhat is an Event History?
An append log of Events that represents the full state a Workflow Execution.
Learn more.
A Go-based Activity Definition can return either just an error
or a customValue, error
combination (same as a Workflow Definition).
You may wish to use a struct
type to hold all custom values, just keep in mind they must all be serializable.
// YourActivityResultObject is the struct returned from your Activity.
// Use a struct so that you can return multiple values of different types.
// Additionally, your function signature remains compatible if the fields change.
type YourActivityResultObject struct {
ResultFieldX string
ResultFieldY int
}
// ...
func (a *YourActivityObject) YourActivityDefinition(ctx context.Context, param YourActivityParam) (*YourActivityResultObject, error) {
// ...
result := &YourActivityResultObject{
ResultFieldX: "Success",
ResultFieldY: 1,
}
// Return the results back to the Workflow Execution.
// The results persist within the Event History of the Workflow Execution.
return result, nil
}
Activity Type
Activities have a Type that are referred to as the Activity name. The following examples demonstrate how to set a custom name for your Activity Type.
View source codefunc main() {
// ...
yourWorker := worker.New(temporalClient, "your-custom-task-queue-name", worker.Options{})
// ...
// Use RegisterOptions to change the name of the Activity Type for example.
registerAOptions := activity.RegisterOptions{
Name: "JustAnotherActivity",
}
yourWorker.RegisterActivityWithOptions(yourapp.YourSimpleActivityDefinition, registerAOptions)
// Run the Worker
err = yourWorker.Run(worker.InterruptCh())
// ...
}
Activity Execution
Calls to spawn Activity ExecutionsWhat is an Activity Execution?
An Activity Execution is the full chain of Activity Task Executions.
Learn more are written within a Workflow DefinitionWhat is a Workflow Definition?
A Workflow Definition is the code that defines the constraints of a Workflow Execution.
Learn more.
The call to spawn an Activity Execution generates the ScheduleActivityTask Command.
This results in the set of three Activity TaskWhat is an Activity Task?
An Activity Task contains the context needed to make an Activity Task Execution.
Learn more related Events (ActivityTaskScheduled, ActivityTaskStarted, and ActivityTask[Closed])in your Workflow Execution Event History.
A single instance of the Activities implementation is shared across multiple simultaneous Activity invocations. Therefore, the Activity implementation code must be stateless.
The values passed to Activities through invocation parameters or returned through a result value are recorded in the Execution history. The entire Execution history is transferred from the Temporal service to Workflow Workers when a Workflow state needs to recover. A large Execution history can thus adversely impact the performance of your Workflow.
Therefore, be mindful of the amount of data you transfer through Activity invocation parameters or Return Values. Otherwise, no additional limitations exist on Activity implementations.
To spawn an Activity ExecutionWhat is an Activity Execution?
An Activity Execution is the full chain of Activity Task Executions.
Learn more, call ExecuteActivity()
inside your Workflow Definition.
The API is available from the go.temporal.io/sdk/workflow
package.
The ExecuteActivity()
API call requires an instance of workflow.Context
, the Activity function name, and any variables to be passed to the Activity Execution.
The Activity function name can be provided as a variable object (no quotations) or as a string.
The benefit of passing the actual function object is that the framework can validate the parameters against the Activity Definition.
The ExecuteActivity
call returns a Future, which can be used to get the result of the Activity Execution.
func YourWorkflowDefinition(ctx workflow.Context, param YourWorkflowParam) (*YourWorkflowResultObject, error) {
// Set the options for the Activity Execution.
// Either StartToClose Timeout OR ScheduleToClose is required.
// Not specifying a Task Queue will default to the parent Workflow Task Queue.
activityOptions := workflow.ActivityOptions{
StartToCloseTimeout: 10 * time.Second,
}
ctx = workflow.WithActivityOptions(ctx, activityOptions)
activityParam := YourActivityParam{
ActivityParamX: param.WorkflowParamX,
ActivityParamY: param.WorkflowParamY,
}
// Use a nil struct pointer to call Activities that are part of a struct.
var a *YourActivityObject
// Execute the Activity and wait for the result.
var activityResult *YourActivityResultObject
err := workflow.ExecuteActivity(ctx, a.YourActivityDefinition, activityParam).Get(ctx, &activityResult)
if err != nil {
return nil, err
}
// ...
}
Required timeout
Activity Execution semantics rely on several parameters.
The only required value that needs to be set is either a Schedule-To-Close TimeoutWhat is a Start-To-Close Timeout?
A Start-To-Close Timeout is the maximum time allowed for a single Activity Task Execution.
Learn more or a Start-To-Close TimeoutWhat is a Start-To-Close Timeout?
A Start-To-Close Timeout is the maximum time allowed for a single Activity Task Execution.
Learn more.
These values are set in the Activity Options.
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 {
// ...
}
Get Activity results
The call to spawn an Activity ExecutionWhat is an Activity Execution?
An Activity Execution is the full chain of Activity Task Executions.
Learn more generates the ScheduleActivityTask Command and provides the Workflow with an Awaitable.
Workflow Executions can either block progress until the result is available through the Awaitable or continue progressing, making use of the result when it becomes available.
The ExecuteActivity
API call returns an instance of workflow.Future
which has the following two methods:
Get()
: Takes an instance of theworkflow.Context
, that was passed to the Activity Execution, and a pointer as parameters. The variable associated with the pointer is populated with the Activity Execution result. This call blocks until the results are available.IsReady()
: Returnstrue
when the result of the Activity Execution is ready.
Call the Get()
method on the instance of workflow.Future
to get the result of the Activity Execution.
The type of the result parameter must match the type of the return value declared by the Activity function.
func YourWorkflowDefinition(ctx workflow.Context, param YourWorkflowParam) (YourWorkflowResponse, error) {
// ...
future := workflow.ExecuteActivity(ctx, YourActivityDefinition, yourActivityParam)
var yourActivityResult YourActivityResult
if err := future.Get(ctx, &yourActivityResult); err != nil {
// ...
}
// ...
}
Use the IsReady()
method first to make sure the Get()
call doesn't cause the Workflow Execution to wait on the result.
func YourWorkflowDefinition(ctx workflow.Context, param YourWorkflowParam) (YourWorkflowResponse, error) {
// ...
future := workflow.ExecuteActivity(ctx, YourActivityDefinition, yourActivityParam)
// ...
if(future.IsReady()) {
var yourActivityResult YourActivityResult
if err := future.Get(ctx, &yourActivityResult); err != nil {
// ...
}
}
// ...
}
It is idiomatic to invoke multiple Activity Executions from within a Workflow. Therefore, it is also idiomatic to either block on the results of the Activity Executions or continue on to execute additional logic, checking for the Activity Execution results at a later time.
Develop Worker
Create an instance of Worker
by calling worker.New()
, available through the go.temporal.io/sdk/worker
package, and pass it the following parameters:
- An instance of the Temporal Go SDK
Client
. - The name of the Task Queue that it will poll.
- An instance of
worker.Options
, which can be empty.
Then, register the Workflow Types and the Activity Types that the Worker will be capable of executing.
Lastly, call either the Start()
or the Run()
method on the instance of the Worker.
Run accepts an interrupt channel as a parameter, so that the Worker can be stopped in the terminal.
Otherwise, the Stop()
method must be called to stop the Worker.
If you have gow
installed, the Worker Process automatically "reloads" when you update the Worker file:
go install github.com/mitranim/gow@latest
gow run worker/main.go # automatically reloads when file changes
package main
import (
"log"
"go.temporal.io/sdk/activity"
"go.temporal.io/sdk/client"
"go.temporal.io/sdk/worker"
"go.temporal.io/sdk/workflow"
"documentation-samples-go/yourapp"
)
func main() {
// Create a Temporal Client
// A Temporal Client is a heavyweight object that should be created just once per process.
temporalClient, err := client.Dial(client.Options{})
if err != nil {
log.Fatalln("Unable to create client", err)
}
defer temporalClient.Close()
// Create a new Worker.
yourWorker := worker.New(temporalClient, "your-custom-task-queue-name", worker.Options{})
// Register your Workflow Definitions with the Worker.
// Use the ReisterWorkflow or RegisterWorkflowWithOptions method for each Workflow registration.
yourWorker.RegisterWorkflow(yourapp.YourWorkflowDefinition)
// ...
// Register your Activity Definitons with the Worker.
// Use this technique for registering all Activities that are part of a struct and set the shared variable values.
message := "This could be a connection string or endpoint details"
number := 100
activities := &yourapp.YourActivityObject{
Message: &message,
Number: &number,
}
// Use the RegisterActivity or RegisterActivityWithOptions method for each Activity.
yourWorker.RegisterActivity(activities)
// ...
// Run the Worker
err = yourWorker.Run(worker.InterruptCh())
if err != nil {
log.Fatalln("Unable to start Worker", err)
}
}
// ...
Run a Temporal Cloud Worker
To run a Worker that uses Temporal Cloud, you need to provide additional connection and client options that include the following:
- An address that includes your Cloud Namespace NameWhat is a Namespace?
A Namespace is a unit of isolation within the Temporal Platform
Learn more and a port number:<Namespace>.<ID>.tmprl.cloud:<port>
. - mTLS CA certificate.
- mTLS private key.
For more information about managing and generating client certificates for Temporal Cloud, see How to manage certificates in Temporal Cloud.
For more information about configuring TLS to secure inter- and intra-network communication for a Temporal Cluster, see Temporal Customization Samples.
To run a Worker that talks to Temporal Cloud, you need the following:
- A compatible mTLS CA certificate and mTLS private key that has been added to your Namespace.
See certificate requirementsRequirements for CA certificates in Temporal Cloud
Certificates provided to Temporal for your Namespaces must meet certain requirements.
Learn more. - Your Temporal Cloud Namespace IdWhat is a Cloud Namespace Id?
A Cloud Namespace Id is a globally unique identifier for a Namespace in Temporal Cloud.
Learn more, which includes your Temporal Cloud Namespace NameWhat is a Cloud Namespace Name?
A Cloud Namespace Name is a customer-supplied name for a Namespace in Temporal Cloud.
Learn more and the unique five- or six-digit Temporal Cloud Account IdWhat is a Temporal Cloud Account Id?
A Temporal Cloud Account Id is a unique identifier for a customer.
Learn more that is appended to it. This information can be found in the URL of your Namespace; for example,https://cloud.temporal.io/namespaces/yournamespace.a2fx6/
. Remember that the Namespace Id must include the Account Id:yournamespace.a2fx6
.
For more information about managing and generating client certificates for Temporal Cloud, see How to manage certificates in Temporal Cloud.
For more information about configuring TLS to secure inter- and intra-network communication for a Temporal Cluster, see Temporal Customization Samples.
View source codepackage main
import (
"crypto/tls"
"log"
"go.temporal.io/sdk/client"
"go.temporal.io/sdk/worker"
"documentation-samples-go/cloud"
)
func main() {
// Get the key and cert from your env or local machine
clientKeyPath := "./secrets/yourkey.key"
clientCertPath := "./secrets/yourcert.pem"
// Specify the host and port of your Temporal Cloud Namespace
// Host and port format: namespace.unique_id.tmprl.cloud:port
hostPort := "<yournamespace>.<id>.tmprl.cloud:7233"
namespace := "<yournamespace>.<id>"
// Use the crypto/tls package to create a cert object
cert, err := tls.LoadX509KeyPair(clientCertPath, clientKeyPath)
if err != nil {
log.Fatalln("Unable to load cert and key pair.", err)
}
// Add the cert to the tls certificates in the ConnectionOptions of the Client
temporalClient, err := client.Dial(client.Options{
HostPort: hostPort,
Namespace: namespace,
ConnectionOptions: client.ConnectionOptions{
TLS: &tls.Config{Certificates: []tls.Certificate{cert}},
},
})
if err != nil {
log.Fatalln("Unable to connect to Temporal Cloud.", err)
}
defer temporalClient.Close()
// Create a new Worker.
yourWorker := worker.New(temporalClient, "cloud-connection-example-go-task-queue", worker.Options{})
// ...
}
Register types
All Workers listening to the same Task Queue name must be registered to handle the exact same Workflows Types and Activity Types.
If a Worker polls a Task for a Workflow Type or Activity Type it does not know about, it fails that Task. However, the failure of the Task does not cause the associated Workflow Execution to fail.
The RegisterWorkflow()
and RegisterActivity()
calls essentially create an in-memory mapping between the Workflow Types and their implementations, inside the Worker process.
Registering Activity structs
Per Activity DefinitionHow to develop an Activity Definition in Go
In the Temporal Go SDK programming model, an Activity Definition is an exportable function or a struct
method.
Learn more best practices, you might have an Activity struct that has multiple methods and fields.
When you use RegisterActivity()
for an Activity struct, that Worker has access to all exported methods.
Registering multiple Types
To register multiple Activity Types and/or Workflow Types with the Worker Entity, just make multiple Activity registration calls, but make sure each Type name is unique:
w.RegisterActivity(ActivityA)
w.RegisterActivity(ActivityB)
w.RegisterActivity(ActivityC)
w.RegisterWorkflow(WorkflowA)
w.RegisterWorkflow(WorkflowB)
w.RegisterWorkflow(WorkflowC)
Start Workflow Execution
Workflow Execution semantics rely on several parameters—that is, to start a Workflow Execution you must supply a Task Queue that will be used for the Tasks (one that a Worker is polling), the Workflow Type, language-specific contextual data, and Workflow Function parameters.
In the examples below, all Workflow Executions are started using a Temporal Client. To spawn Workflow Executions from within another Workflow Execution, use either the Child Workflow or External Workflow APIs.
See the Customize Workflow Type section to see how to customize the name of the Workflow Type.
A request to spawn a Workflow Execution causes the Temporal Cluster to create the first Event (WorkflowExecutionStarted) in the Workflow Execution Event History. The Temporal Cluster then creates the first Workflow Task, resulting in the first WorkflowTaskScheduled Event.
To spawn a Workflow Execution, use the ExecuteWorkflow()
method on the Go SDK Client
.
The ExecuteWorkflow()
API call requires an instance of context.Context
, an instance of StartWorkflowOptions
, a Workflow Type name, and all variables to be passed to the Workflow Execution.
The ExecuteWorkflow()
call returns a Future, which can be used to get the result of the Workflow Execution.
package main
import (
// ...
"go.temporal.io/sdk/client"
)
func main() {
temporalClient, err := client.Dial(client.Options{})
if err != nil {
// ...
}
defer temporalClient.Close()
// ...
workflowOptions := client.StartWorkflowOptions{
// ...
}
workflowRun, err := temporalClient.ExecuteWorkflow(context.Background(), workflowOptions, YourWorkflowDefinition, param)
if err != nil {
// ...
}
// ...
}
func YourWorkflowDefinition(ctx workflow.Context, param YourWorkflowParam) (YourWorkflowResponse, error) {
// ...
}
If the invocation process has access to the function directly, then the Workflow Type name parameter can be passed as if the function name were a variable, without quotations.
workflowRun, err := temporalClient.ExecuteWorkflow(context.Background(), workflowOptions, YourWorkflowDefinition, param)
If the invocation process does not have direct access to the statically defined Workflow Definition, for example, if the Workflow Definition is in an un-importable package, or it is written in a completely different language, then the Workflow Type can be provided as a string
.
workflowRun, err := c.ExecuteWorkflow(context.Background(), workflowOptions, "YourWorkflowDefinition", param)
Set Task Queue
In most SDKs, the only Workflow Option that must be set is the name of the Task QueueWhat is a Task Queue?
A Task Queue is a first-in, first-out queue that a Worker Process polls for Tasks.
Learn more.
For any code to execute, a Worker Process must be running that contains a Worker Entity that is polling the same Task Queue name.
Create an instance of StartWorkflowOptions
from the go.temporal.io/sdk/client
package, set the TaskQueue
field, and pass the instance to the ExecuteWorkflow
call.
- Type:
string
- Default: None, this is a required field to be set by the developer
workflowOptions := client.StartWorkflowOptions{
// ...
TaskQueue: "your-task-queue",
// ...
}
workflowRun, err := c.ExecuteWorkflow(context.Background(), workflowOptions, YourWorkflowDefinition)
if err != nil {
// ...
}
Workflow Id
Although it is not required, we recommend providing your own Workflow IdWhat is a Workflow Id?
A Workflow Id is a customizable, application-level identifier for a Workflow Execution that is unique to an Open Workflow Execution within a Namespace.
Learn more that maps to a business process or business entity identifier, such as an order identifier or customer identifier.
Create an instance of StartWorkflowOptions
from the go.temporal.io/sdk/client
package, set the ID
field, and pass the instance to the ExecuteWorkflow
call.
- Type:
string
- Default: System generated UUID
workflowOptions := client.StartWorkflowOptions{
// ...
ID: "Your-Custom-Workflow-Id",
// ...
}
workflowRun, err := c.ExecuteWorkflow(context.Background(), workflowOptions, YourWorkflowDefinition)
if err != nil {
// ...
}
Get Workflow results
If the call to start a Workflow Execution is successful, you will gain access to the Workflow Execution's Run Id.
The Workflow Id, Run Id, and Namespace may be used to uniquely identify a Workflow Execution in the system and get its result.
It's possible to both block progress on the result (synchronous execution) or get the result at some other point in time (asynchronous execution).
In the Temporal Platform, it's also acceptable to use Queries as the preferred method for accessing the state and results of Workflow Executions.
The ExecuteWorkflow
call returns an instance of WorkflowRun
, which is the workflowRun
variable in the following line.
workflowRun, err := c.ExecuteWorkflow(context.Background(), workflowOptions, app.YourWorkflowDefinition, param)
if err != nil {
// ...
}
// ...
}
The instance of WorkflowRun
has the following three methods:
GetWorkflowID()
: Returns the Workflow Id of the invoked Workflow Execution.GetRunID()
: Always returns the Run Id of the initial Run (See Continue As New) in the series of Runs that make up the full Workflow Execution.Get
: Takes a pointer as a parameter and populates the associated variable with the Workflow Execution result.
To wait on the result of Workflow Execution in the same process that invoked it, call Get()
on the instance of WorkflowRun
that is returned by the ExecuteWorkflow()
call.
workflowRun, err := c.ExecuteWorkflow(context.Background(), workflowOptions, YourWorkflowDefinition, param)
if err != nil {
// ...
}
var result YourWorkflowResponse
err = workflowRun.Get(context.Background(), &result)
if err != nil {
// ...
}
// ...
}
However, the result of a Workflow Execution can be obtained from a completely different process. All that is needed is the Workflow Id. (A Run Id is optional if more than one closed Workflow Execution has the same Workflow Id.) The result of the Workflow Execution is available for as long as the Workflow Execution Event History remains in the system.
Call the GetWorkflow()
method on an instance of the Go SDK Client and pass it the Workflow Id used to spawn the Workflow Execution.
Then call the Get()
method on the instance of WorkflowRun
that is returned, passing it a pointer to populate the result.
// ...
workflowID := "Your-Custom-Workflow-Id"
workflowRun := c.GetWorkflow(context.Background, workflowID)
var result YourWorkflowResponse
err = workflowRun.Get(context.Background(), &result)
if err != nil {
// ...
}
// ...
Get last completion result
In the case of a Temporal Cron JobWhat is a Temporal Cron Job?
A Temporal Cron Job is the series of Workflow Executions that occur when a Cron Schedule is provided in the call to spawn a Workflow Execution.
Learn more, you might need to get the result of the previous Workflow Run and use it in the current Workflow Run.
To do this, use the HasLastCompletionResult
and GetLastCompletionResult
APIs, available from the go.temporal.io/sdk/workflow
package, directly in your Workflow code.
type CronResult struct {
Count int
}
func YourCronWorkflowDefinition(ctx workflow.Context) (CronResult, error) {
count := 1
if workflow.HasLastCompletionResult(ctx) {
var lastResult CronResult
if err := workflow.GetLastCompletionResult(ctx, &lastResult); err == nil {
count = count + lastResult.Count
}
}
newResult := CronResult {
Count: count,
}
return newResult, nil
}
This will work even if one of the cron Workflow Runs fails. The next Workflow Run gets the result of the last successfully Completed Workflow Run.