Skip to main content

Updates

An update is implemented as a method on the workflow struct.

info

Workflow Updates are considered an experimental feature. They require the --workflow-update-enabled to be enabled.

example.proto
syntax="proto3";

package example.v1;

import "temporal/v1/temporal.proto";

service Example {
option (temporal.v1.service).task_queue = "example-v1";

rpc Foo(FooInput) returns (FooOutput) {
option (temporal.v1.workflow) = {
update: { ref: "Bar" }
};
}

rpc Bar(BarInput) returns (BarOutput) {
option (temporal.v1.update) = {
id: 'bar/${! uuid_v4() }'
};
}
}

Validation

Temporal workflow updates have four phases: Admission, Validation, Execution, and Completion. The Validation phase is optional, and can be enabled by specifying a developer-provided validation function.

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.

Validation is enabled using the validate update option and implemented as a method on the workflow struct.

example.proto
syntax="proto3";

package example.v1;

import "temporal/v1/temporal.proto";

service Example {
option (temporal.v1.service).task_queue = "example-v1";

rpc Foo(FooInput) returns (FooOutput) {
option (temporal.v1.workflow) = {
update: { ref: "Bar" }
};
}

rpc Bar(BarInput) returns (BarOutput) {
option (temporal.v1.update) = {
id: 'bar/${! uuid_v4() }'
validate: true
};
}
}

Invocation

Client

Consumers can utilize the generated Client to execute workflow updates from any Go application. See the Clients guide for more usage details.

example.go
package example

import (
"context"
"log"

examplev1 "path/to/gen/example/v1"
"go.temporal.io/sdk/client"
)

func main() {
c, _ := client.Dial(client.Options{})
client, ctx := examplev1.NewExampleClient(c), context.Background()

bar, err := client.Bar(ctx, "foo-worklow-id", "", &examplev1.BarInput{});
if err != nil {
log.Fatalf("error updating workflow: %v", err)
}
log.Printf("bar update successful: %s", bar.String())
}

Command Line Interface (CLI)

Consumers can utilize the generated Command Line Interface as a standalone application for executing workflow updates. See the CLI guide for more usage details.

example -h
NAME:
example - an example temporal cli

USAGE:
example [global options] command [command options] [arguments...]

COMMANDS:
help, h Shows a list of commands or help for one command
UPDATES:
bar Bar updates a foo workflow
WORKFLOWS:
foo Foo does some foo thing
example bar -h
NAME:
example bar - Bar updates a foo workflow

USAGE:
example bar [command options] [arguments...]

CATEGORY:
UPDATES

OPTIONS:
--detach, -d run workflow update in the background and print workflow, execution, and update id (default: false)
--input-file value, -f value path to json-formatted input file
--run-id value, -r value run id
--workflow-id value, -w value workflow id

INPUT

--name value Name specifies the subject to greet
example bar -w foo/test --name Temporal
{}

Workflow Run

The generated client's asynchronous workflow methods return a WorkflowRun that provides methods for updating the workflow.

package main

import (
"context"
"log"

examplev1 "path/to/gen/example/v1"
"go.temporal.io/sdk/client"
)

func main() {
c, _ := client.Dial(client.Options{})
client, ctx := examplev1.NewExampleClient(c), context.Background()

run, err := client.FooAsync(ctx, &examplev1.FooInput{})
if err != nil {
log.Fatalf("error starting workflow: %v", err)
}

bar, err := run.Bar(ctx, &examplev1.BarInput{})
if err != nil {
log.Fatalf("error updating workflow: %v", err)
}
log.Printf("update successful: %s\n", bar.String())

foo, err := run.Get(ctx)
if err != nil {
log.Fatalf("workflow failed: %v", err)
}
log.Printf("workflow successful: %s\n", foo.String())
}

Cross-Namespace (XNS)

Updates can be executed from other workflows in a different Temporal namespace or even an entirely separate Temporal cluster (e.g. on-prem to cloud) using the generated Cross-Namespace helpers. See the Cross-Namespace guide for more usage details.

example.go
package example

import (
"fmt"

examplev1 "path/to/gen/example/v1"
"path/to/gen/example/v1/examplev1xns"
"go.temporal.io/sdk/workflow"
)

func MyWorkflow(ctx workflow.Context) error {
_, err := examplev1xns.Bar(ctx, "foo-workflow-id", "", &examplev1.BarInput{});
if err != nil {
return fmt.Errorf("error executing Bar update: %w", err)
}
return err
}

SDK

Proto updates are compatible with official Temporal sdk update methods.

example.go
package example

import (
"context"
"log"

examplev1 "path/to/gen/example/v1"
"go.temporal.io/sdk/client"
)

func main() {
c, _ := client.Dial(client.Options{})
ctx := context.Background()

handle, err := client.UpdateWorkflow(ctx, "foo-worklow-id", "", examplev1.BarUpdateName, &examplev1.BarInput{});
if err != nil {
log.Fatalf("error starting workflow update: %v", err)
}

var bar examplev1.BarOutput
if err := handle.Get(ctx, &bar); err != nil {
log.Fatalf("error updating workflow: %v", err)
}
log.Printf("bar update successful: %s", bar.String())
}

UpdateOptions

Both synchronous and asynchronous update helpers accept an optional <Update>Options value as the final argument. This argument can be used to override the default client.UpdateWorkflowWithOptionsRequest created using the defaults defined in the schema.

WithUpdateID

Sets the update UpdateID value

example.go
func example(ctx context.Context, client examplev1.ExampleClient) error {
_, err := client.Bar(ctx, "foo-workflow-id", "", &examplev1.BarInput{}, examplev1.NewBarOptions().
WithUpdateID("bar/baz"),
)
return err
}

WithUpdateWorkflowOptions

Override the initial client.UpdateWorkflowWithOptionsRequest value for an individual invocation. Schema defined defaults will be applied over this value.

example.go
func example(ctx context.Context, client examplev1.ExampleClient) error {
_, err := client.Bar(ctx, "foo-workflow-id", "", &examplev1.BarInput{}, examplev1.NewBarOptions().
WithUpdateWorkflowOptions(client.UpdateWorkflowWithOptionsRequest{
WaitPolicy: &updatepb.WaitPolicy{
LifecycleStage: enumspb.UPDATE_WORKFLOW_EXECUTION_LIFECYCLE_STAGE_ACCEPTED,
},
}),
)
return err
}

WithWaitPolicy

Sets the update WaitPolicy value

example.go
func example(ctx context.Context, client examplev1.ExampleClient) error {
_, err := client.Bar(ctx, "foo-workflow-id", "", &examplev1.BarInput{}, examplev1.NewBarOptions().
WithUpdateWorkflowOptions(enumspb.UPDATE_WORKFLOW_EXECUTION_LIFECYCLE_STAGE_ACCEPTED),
)
return err
}