Signals
A signal is implemented as a struct value included on the workflow input.
- Schema
- Go
syntax="proto3";
package example.v1;
import "google/protobuf/empty.proto";
import "temporal/v1/temporal.proto";
service Example {
rpc Foo(FooInput) returns (FooOutput) {
option (temporal.v1.workflow) = {
signal: { ref: "Bar" }
};
}
rpc Bar(BarInput) returns (google.protobuf.Empty) {
option (temporal.v1.signal) = {};
}
}
package example
import (
examplev1 "path/to/gen/example/v1"
"go.temporal.io/sdk/workflow"
)
type FooWorkflow struct {
examplev1.FooWorkflowInput
}
func (w *FooWorkflow) Execute(ctx workflow.Context) (*examplev1.FooOutput, error) {
bar, _ := w.Bar.Receive(ctx)
workflow.GetLogger(ctx).Info("received Bar signal", "bar", bar)
return &examplev1.FooOutput{}, nil
}
Fields
ReceiveChannel
The underlying channel associated with the signal.
- Go
- Schema
package example
import (
examplev1 "path/to/gen/example/v1"
"go.temporal.io/sdk/workflow"
)
type FooWorkflow struct {
*examplev1.FooWorkflowInput
}
func (w *FooWorkflow) Execute(ctx workflow.Context) (*examplev1.FooOutput, error) {
workflow.NewSelector(ctx).
AddReceive(w.Bar.ReceiveChannel, func(workflow.ReceiveChannel, bool) {
bar := w.Bar.ReceiveAsync()
workflow.GetLogger(ctx).Info("received Bar signal", "bar", bar)
}).
AddReceive(w.Baz.ReceiveChannel, func(workflow.ReceiveChannel, bool) {
baz := w.Baz.ReceiveAsync()
workflow.GetLogger(ctx).Info("received Baz signal", "baz", baz)
}).
Select(ctx)
return &examplev1.FooOutput{}, nil
}
syntax="proto3";
package example.v1;
import "google/protobuf/empty.proto";
import "temporal/v1/temporal.proto";
service Example {
rpc Foo(FooInput) returns (FooOutput) {
option (temporal.v1.workflow) = {
signal: { ref: "Bar" }
signal: { ref: "Baz" }
};
}
rpc Bar(BarInput) returns (google.protobuf.Empty) {
option (temporal.v1.signal) = {};
}
rpc Baz(BazInput) returns (google.protobuf.Empty) {
option (temporal.v1.signal) = {};
}
}
Methods
Signal structs provide several typed methods for use within workflow definitions.
Receive
Blocks on signal receive, returning the signal payload, if defined, and a boolean value indicating whether or not there are more pending signals.
- Go
- Schema
package example
import (
examplev1 "path/to/gen/example/v1"
"go.temporal.io/sdk/workflow"
)
type FooWorkflow struct {
*examplev1.FooWorkflowInput
}
func (w *FooWorkflow) Execute(ctx workflow.Context) (*examplev1.FooOutput, error) {
bar, more := w.Bar.Receive(ctx)
workflow.GetLogger(ctx).Info("received Bar signal", "bar", bar, "more", more)
return &examplev1.FooOutput{}, nil
}
syntax="proto3";
package example.v1;
import "google/protobuf/empty.proto";
import "temporal/v1/temporal.proto";
service Example {
rpc Foo(FooInput) returns (FooOutput) {
option (temporal.v1.workflow) = {
signal: { ref: "Bar" }
};
}
rpc Bar(BarInput) returns (google.protobuf.Empty) {
option (temporal.v1.signal) = {};
}
}
ReceiveAsync
Attempts to receive from the underlying channel without blocking. If the signal definition specifies a non empty input, this method returns a pointer to the input payload, which will be nil
in the case of no signal available. If the signal specifies an empty input, the method returns a boolean value, with false indicating no signal available.
- Go
- Schema
package example
import (
examplev1 "path/to/gen/example/v1"
"go.temporal.io/sdk/workflow"
)
type FooWorkflow struct {
*examplev1.FooWorkflowInput
}
func (w *FooWorkflow) Execute(ctx workflow.Context) (*examplev1.FooOutput, error) {
if bar := w.Bar.ReceiveAsync(ctx); bar != nil {
workflow.GetLogger(ctx).Info("received Bar signal-with-start", "bar", bar)
} else {
workflow.GetLogger(ctx).Info("no Bar signal received")
}
return &examplev1.FooOutput{}, nil
}
syntax="proto3";
package example.v1;
import "google/protobuf/empty.proto";
import "temporal/v1/temporal.proto";
service Example {
rpc Foo(FooInput) returns (FooOutput) {
option (temporal.v1.workflow) = {
signal: { ref: "Bar", start: true }
};
}
rpc Bar(BarInput) returns (google.protobuf.Empty) {
option (temporal.v1.signal) = {};
}
}
ReceiveWithTimeout
Blocks until signal received or timeout expires. Returns ok
value of false
when no value was found in the channel for the duration of timeout or the ctx was canceled. Returns more
value of false
when Channel is closed.
- Go
- Schema
package example
import (
"time"
examplev1 "path/to/gen/example/v1"
"go.temporal.io/sdk/workflow"
)
type FooWorkflow struct {
*examplev1.FooWorkflowInput
}
func (w *FooWorkflow) Execute(ctx workflow.Context) (*examplev1.FooOutput, error) {
bar, ok, more := w.Bar.ReceiveWithTimeout(ctx, time.Hour)
if !ok {
workflow.GetLogger(ctx).Info("timeout expired or context cancelled")
} else {
workflow.GetLogger(ctx).Info("received Bar signal", "bar", bar, "more", more)
}
return &examplev1.FooOutput{}, nil
}
syntax="proto3";
package example.v1;
import "google/protobuf/empty.proto";
import "temporal/v1/temporal.proto";
service Example {
rpc Foo(FooInput) returns (FooOutput) {
option (temporal.v1.workflow) = {
signal: { ref: "Bar" }
};
}
rpc Bar(BarInput) returns (google.protobuf.Empty) {
option (temporal.v1.signal) = {};
}
}
Select
Checks for a signal without blocking.
- Go
- Schema
package example
import (
"time"
examplev1 "path/to/gen/example/v1"
"go.temporal.io/sdk/workflow"
)
type FooWorkflow struct {
*examplev1.FooWorkflowInput
}
func (w *FooWorkflow) Execute(ctx workflow.Context) (*examplev1.FooOutput, error) {
sel := workflow.NewSelector(ctx)
w.Bar.Select(sel, func(bar *examplev1.BarInput) {
workflow.GetLogger(ctx).Info("received Bar signal", "bar", bar)
})
w.Baz.Select(sel, func(baz *examplev1.BazInput) {
workflow.GetLogger(ctx).Info("received Baz signal", "baz", baz)
})
sel.Select(ctx)
return &examplev1.FooOutput{}, nil
}
syntax="proto3";
package example.v1;
import "google/protobuf/empty.proto";
import "temporal/v1/temporal.proto";
service Example {
rpc Foo(FooInput) returns (FooOutput) {
option (temporal.v1.workflow) = {
signal: { ref: "Bar" }
signal: { ref: "Baz" }
};
}
rpc Bar(BarInput) returns (google.protobuf.Empty) {
option (temporal.v1.signal) = {};
}
rpc Baz(BazInput) returns (google.protobuf.Empty) {
option (temporal.v1.signal) = {};
}
}
Invocation
Client
Consumers can utilize the generated Client to execute workflows from any Go application. See the Clients guide for more usage details.
- Go
- Schema
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()
if err := client.Bar(context.Background(), "foo-worklow-id", "", &examplev1.BarInput{}); err != nil {
log.Fatalf("error signaling workflow: %v", err)
}
}
syntax="proto3";
package example.v1;
import "google/protobuf/empty.proto";
import "temporal/v1/temporal.proto";
service Example {
rpc Foo(FooInput) returns (FooOutput) {
option (temporal.v1.workflow) = {
signal: { ref: "Bar" }
};
}
rpc Bar(BarInput) returns (google.protobuf.Empty) {
option (temporal.v1.signal) = {};
}
}
Command Line Interface (CLI)
Consumers can utilize the generated Command Line Interface as a standalone application for executing workflows. See the CLI guide for more usage details.
- Shell
- Go
- Schema
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
SIGNALS:
bar Bar signals some bar thing
WORKFLOWS:
foo Foo does some foo thing
NAME:
example bar - Bar signals some bar thing
USAGE:
example bar [command options] [arguments...]
CATEGORY:
SIGNALS
OPTIONS:
--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
success
package main
import (
"log"
"os"
examplev1 "path/to/gen/example/v1"
)
func main() {
app, err := examplev1.NewExampleCLI()
if err != nil {
log.Fatalf("error initializing cli: %v", err)
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
syntax="proto3";
package example.v1;
import "google/protobuf/empty.proto";
import "temporal/v1/temporal.proto";
service Example {
// Foo does some foo thing
rpc Foo(FooInput) returns (FooOutput) {
option (temporal.v1.workflow) = {
signal: { ref: "Bar" }
};
}
// Bar signals some bar thing
rpc Bar(BarInput) returns (google.protobuf.Empty) {
option (temporal.v1.signal) = {};
}
}
message BarInput {
string name = 1;
}
Workflow Run
The generated client's asynchronous workflow methods return a WorkflowRun that provides methods for signaling the workflow.
- Go
- Schema
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)
}
if err := run.Bar(ctx, &examplev1.BarInput{}); err != nil {
log.Fatalf("error signalling workflow: %v", err)
}
foo, err := run.Get(ctx)
if err != nil {
log.Fatalf("workflow failed: %v", err)
}
log.Printf("workflow successful: %s\n", foo.String())
}
syntax="proto3";
package example.v1;
import "google/protobuf/empty.proto";
import "temporal/v1/temporal.proto";
service Example {
rpc Foo(FooInput) returns (FooOutput) {
option (temporal.v1.workflow) = {
signal: { ref: "Bar" }
};
}
rpc Bar(BarInput) returns (google.protobuf.Empty) {
option (temporal.v1.signal) = {};
}
}
Child Workflow Run
Similar to workflow runs, asynchronousu child workflow methods return a ChildWorkflowRun that provides methods for signaling the child workflow.
- Go
- Schema
package example
import (
"context"
"fmt"
examplev1 "path/to/gen/example/v1"
"go.temporal.io/sdk/workflow"
)
type (
FooWorkflow struct {
*examplev1.FooWorkflowInput
}
BarWorkflow struct {
*examplev1.BarWorkflowInput
}
)
func (w *FooWorkflow) Execute(ctx workflow.Context) error {
run, err := examplev1.BarChildAsync(ctx, &examplev1.BarInput{})
if err != nil {
return fmt.Errorf("error starting child Bar workflow: %w", err)
} else if _, err = run.WaitStart(ctx); err != nil {
return fmt.Errorf("error waiting for child workflow to start: %w", err)
}
if err := run.Baz(ctx, &examplev1.BazInput{}); err != nil {
return fmt.Errorf("error sending Baz signal to child Bar workflow: %w", err)
}
return run.Get(ctx)
}
func (w *BarWorkflow) Execute(ctx workflow.Context) error {
w.Baz.Receive(ctx)
return nil
}
syntax="proto3";
package example.v1;
import "google/protobuf/empty.proto";
import "temporal/v1/temporal.proto";
service Example {
rpc Foo(FooInput) returns (google.protobuf.Empty) {
option (temporal.v1.workflow) = {};
}
rpc Bar(BarInput) returns (google.protobuf.Empty) {
option (temporal.v1.workflow) = {
signal: { ref: "Baz" }
};
}
rpc Baz(BazInput) returns (google.protobuf.Empty) {
option (temporal.v1.signal) = {};
}
}
Cross-Namespace (XNS)
Signals can be sent 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.
- Go
- Go (XNS Workflow Run)
- Schema
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 {
if err := examplev1xns.Bar(ctx, "foo-workflow-id", "", &examplev1.BarInput{}); err != nil {
return fmt.Errorf("error sending Bar signal: %w", err)
}
return nil
}
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 {
run, err := examplev1xns.FooAsync(ctx, &examplev1.FooInput{})
if err != nil {
return fmt.Errorf("error starting Foo workflow: %w", err)
}
if err := run.Bar(ctx, &examplev1.BarInput{}); err != nil {
return fmt.Errorf("error sending Bar signal: %w", err)
}
return run.Get(ctx)
}
syntax="proto3";
package example.v1;
import "google/protobuf/empty.proto";
import "temporal/v1/temporal.proto";
service Example {
rpc Foo(FooInput) returns (google.protobuf.Empty) {
option (temporal.v1.workflow) = {
signal: { ref: "Bar" }
};
}
rpc Bar(BarInput) returns (google.protobuf.Empty) {
option (temporal.v1.signal) = {};
}
}
SDK
Proto signals are compatible with official Temporal sdk signal methods.
- Client
- Workflow
- Schema
package main
import (
"context"
"log"
examplev1 "path/to/gen/example/v1"
"go.temporal.io/sdk/client"
)
func main() {
c, _ := client.Dial(client.Options{})
ctx := context.Background()
if err := c.SignalWorkflow(
ctx,
"foo-workflow-id",
"",
examplev1.BarSignalName,
&examplev1.BarInput{},
); err != nil {
log.Fatalf("error starting workflow: %v", err)
}
}
package example
import (
examplev1 "path/to/gen/example/v1"
"go.temporal.io/sdk/workflow"
)
func SomeWorkflow(ctx workflow.Context) error {
return workflow.SignalExternalWorkflow(
ctx,
"foo-workflow-id",
"",
examplev1.BarSignalName,
&examplev1.BarInput{},
).Get(ctx, nil)
}
syntax="proto3";
package example.v1;
import "google/protobuf/empty.proto";
import "temporal/v1/temporal.proto";
service Example {
rpc Foo(FooInput) returns (google.protobuf.Empty) {
option (temporal.v1.workflow) = {
signal: { ref: "Bar" }
};
}
rpc Bar(BarInput) returns (google.protobuf.Empty) {
option (temporal.v1.signal) = {};
}
}