Skip to content

Commit ae6a7d6

Browse files
committed
Add self-contained samples: tracing, consistentquery, sideeffect, versioning, crossdomain, ctxpropagation
Signed-off-by: Diana Zawadzki <[email protected]>
1 parent 2fd7776 commit ae6a7d6

36 files changed

+1612
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package main
2+
3+
import (
4+
"time"
5+
6+
"go.uber.org/cadence/workflow"
7+
"go.uber.org/zap"
8+
)
9+
10+
// ConsistentQueryWorkflow demonstrates query handlers with signal handling.
11+
func ConsistentQueryWorkflow(ctx workflow.Context) error {
12+
queryResult := 0
13+
logger := workflow.GetLogger(ctx)
14+
logger.Info("ConsistentQueryWorkflow started")
15+
16+
// Setup query handler for "state" query type
17+
err := workflow.SetQueryHandler(ctx, "state", func(input []byte) (int, error) {
18+
return queryResult, nil
19+
})
20+
if err != nil {
21+
logger.Info("SetQueryHandler failed: " + err.Error())
22+
return err
23+
}
24+
25+
signalChan := workflow.GetSignalChannel(ctx, "increase")
26+
27+
s := workflow.NewSelector(ctx)
28+
s.AddReceive(signalChan, func(c workflow.Channel, more bool) {
29+
c.Receive(ctx, nil)
30+
queryResult += 1
31+
workflow.GetLogger(ctx).Info("Received signal!", zap.String("signal", "increase"))
32+
})
33+
34+
workflow.Go(ctx, func(ctx workflow.Context) {
35+
for {
36+
s.Select(ctx)
37+
}
38+
})
39+
40+
// Wait for timer before completing
41+
workflow.NewTimer(ctx, time.Minute*2).Get(ctx, nil)
42+
logger.Info("Timer fired")
43+
44+
logger.Info("ConsistentQueryWorkflow completed")
45+
return nil
46+
}
47+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!-- THIS IS A GENERATED FILE -->
2+
<!-- PLEASE DO NOT EDIT -->
3+
4+
# Sample Generator
5+
6+
This folder is NOT part of the actual sample. It exists only for contributors who work on this sample. Please disregard it if you are trying to learn about Cadence.
7+
8+
To create a better learning experience for Cadence users, each sample folder is designed to be self contained. Users can view every part of writing and running workflows, including:
9+
10+
* Cadence client initialization
11+
* Worker with workflow and activity registrations
12+
* Workflow starter
13+
* and the workflow code itself
14+
15+
Some samples may have more or fewer parts depending on what they need to demonstrate.
16+
17+
In most cases, the workflow code (e.g. `workflow.go`) is the part that users care about. The rest is boilerplate needed to run that workflow. For each sample folder, the workflow code should be written by hand. The boilerplate can be generated. Keeping all parts inside one folder gives early learners more value because they can see everything together rather than jumping across directories.
18+
19+
## Contributing
20+
21+
* When creating a new sample, follow the steps mentioned in the README file in the main samples folder.
22+
* To update the sample workflow code, edit the workflow file directly.
23+
* To update the worker, client, or other boilerplate logic, edit the generator file. If your change applies to all samples, update the common generator file inside the `template` folder. Edit the generator file in this folder only when the change should affect this sample alone.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
## Consistent Query Sample
2+
3+
This sample demonstrates **consistent queries** with signal handling.
4+
5+
### Start the Workflow
6+
7+
```bash
8+
cadence --env development \
9+
--domain cadence-samples \
10+
workflow start \
11+
--tl cadence-samples-worker \
12+
--et 180 \
13+
--workflow_type cadence_samples.ConsistentQueryWorkflow
14+
```
15+
16+
### Query the Workflow
17+
18+
```bash
19+
cadence --env development \
20+
--domain cadence-samples \
21+
workflow query \
22+
--wid <workflow_id> \
23+
--qt state
24+
```
25+
26+
### Send Signals to Update State
27+
28+
```bash
29+
cadence --env development \
30+
--domain cadence-samples \
31+
workflow signal \
32+
--wid <workflow_id> \
33+
--name increase
34+
```
35+
36+
Each signal increments the counter. Query to see the updated value.
37+
38+
### Key Concept: Query + Signal
39+
40+
```go
41+
queryResult := 0
42+
43+
// Register query handler
44+
workflow.SetQueryHandler(ctx, "state", func() (int, error) {
45+
return queryResult, nil
46+
})
47+
48+
// Handle signals that modify state
49+
signalChan := workflow.GetSignalChannel(ctx, "increase")
50+
signalChan.Receive(ctx, nil)
51+
queryResult += 1 // State changes are visible to queries
52+
```
53+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package main
2+
3+
import "github.com/uber-common/cadence-samples/new_samples/template"
4+
5+
func main() {
6+
data := template.TemplateData{
7+
SampleName: "Consistent Query",
8+
Workflows: []string{"ConsistentQueryWorkflow"},
9+
Activities: []string{},
10+
}
11+
template.GenerateAll(data)
12+
}
13+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// THIS IS A GENERATED FILE
2+
// PLEASE DO NOT EDIT
3+
4+
package main
5+
6+
import (
7+
"fmt"
8+
"os"
9+
"os/signal"
10+
"syscall"
11+
)
12+
13+
func main() {
14+
StartWorker()
15+
16+
done := make(chan os.Signal, 1)
17+
signal.Notify(done, syscall.SIGINT)
18+
fmt.Println("Cadence worker started, press ctrl+c to terminate...")
19+
<-done
20+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// THIS IS A GENERATED FILE
2+
// PLEASE DO NOT EDIT
3+
4+
// Package worker implements a Cadence worker with basic configurations.
5+
package main
6+
7+
import (
8+
"github.com/uber-go/tally"
9+
apiv1 "github.com/uber/cadence-idl/go/proto/api/v1"
10+
"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient"
11+
12+
"go.uber.org/cadence/compatibility"
13+
"go.uber.org/cadence/worker"
14+
"go.uber.org/cadence/workflow"
15+
"go.uber.org/yarpc"
16+
"go.uber.org/yarpc/peer"
17+
yarpchostport "go.uber.org/yarpc/peer/hostport"
18+
"go.uber.org/yarpc/transport/grpc"
19+
"go.uber.org/zap"
20+
"go.uber.org/zap/zapcore"
21+
)
22+
23+
const (
24+
HostPort = "127.0.0.1:7833"
25+
Domain = "cadence-samples"
26+
// TaskListName identifies set of client workflows, activities, and workers.
27+
// It could be your group or client or application name.
28+
TaskListName = "cadence-samples-worker"
29+
ClientName = "cadence-samples-worker"
30+
CadenceService = "cadence-frontend"
31+
)
32+
33+
// StartWorker creates and starts a basic Cadence worker.
34+
func StartWorker() {
35+
logger, cadenceClient := BuildLogger(), BuildCadenceClient()
36+
workerOptions := worker.Options{
37+
Logger: logger,
38+
MetricsScope: tally.NewTestScope(TaskListName, nil),
39+
}
40+
41+
w := worker.New(
42+
cadenceClient,
43+
Domain,
44+
TaskListName,
45+
workerOptions)
46+
// HelloWorld workflow registration
47+
w.RegisterWorkflowWithOptions(ConsistentQueryWorkflow, workflow.RegisterOptions{Name: "cadence_samples.ConsistentQueryWorkflow"})
48+
49+
err := w.Start()
50+
if err != nil {
51+
panic("Failed to start worker: " + err.Error())
52+
}
53+
logger.Info("Started Worker.", zap.String("worker", TaskListName))
54+
55+
}
56+
57+
func BuildCadenceClient(dialOptions ...grpc.DialOption) workflowserviceclient.Interface {
58+
grpcTransport := grpc.NewTransport()
59+
// Create a single peer chooser that identifies the host/port and configures
60+
// a gRPC dialer with TLS credentials
61+
myChooser := peer.NewSingle(
62+
yarpchostport.Identify(HostPort),
63+
grpcTransport.NewDialer(dialOptions...),
64+
)
65+
outbound := grpcTransport.NewOutbound(myChooser)
66+
67+
dispatcher := yarpc.NewDispatcher(yarpc.Config{
68+
Name: ClientName,
69+
Outbounds: yarpc.Outbounds{
70+
CadenceService: {Unary: outbound},
71+
},
72+
})
73+
if err := dispatcher.Start(); err != nil {
74+
panic("Failed to start dispatcher: " + err.Error())
75+
}
76+
77+
clientConfig := dispatcher.ClientConfig(CadenceService)
78+
79+
// Create a compatibility adapter that wraps proto-based YARPC clients
80+
// to provide a unified interface for domain, workflow, worker, and visibility APIs
81+
return compatibility.NewThrift2ProtoAdapter(
82+
apiv1.NewDomainAPIYARPCClient(clientConfig),
83+
apiv1.NewWorkflowAPIYARPCClient(clientConfig),
84+
apiv1.NewWorkerAPIYARPCClient(clientConfig),
85+
apiv1.NewVisibilityAPIYARPCClient(clientConfig),
86+
)
87+
}
88+
89+
func BuildLogger() *zap.Logger {
90+
config := zap.NewDevelopmentConfig()
91+
config.Level.SetLevel(zapcore.InfoLevel)
92+
93+
var err error
94+
logger, err := config.Build()
95+
if err != nil {
96+
panic("Failed to setup logger: " + err.Error())
97+
}
98+
99+
return logger
100+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/google/uuid"
8+
"go.uber.org/cadence/activity"
9+
"go.uber.org/cadence/workflow"
10+
"go.uber.org/zap"
11+
)
12+
13+
// Configuration for cross-domain execution
14+
const (
15+
ChildDomain = "child-domain" // Must be registered separately
16+
ChildTaskList = "child-task-list"
17+
)
18+
19+
// CrossDomainData is passed between workflows
20+
type CrossDomainData struct {
21+
Value string
22+
}
23+
24+
// CrossDomainWorkflow demonstrates executing child workflows in different domains.
25+
func CrossDomainWorkflow(ctx workflow.Context) error {
26+
logger := workflow.GetLogger(ctx)
27+
logger.Info("CrossDomainWorkflow started")
28+
29+
// Execute child workflow in a different domain
30+
childCtx := workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{
31+
Domain: ChildDomain,
32+
WorkflowID: "child-wf-" + uuid.New().String(),
33+
TaskList: ChildTaskList,
34+
ExecutionStartToCloseTimeout: time.Minute,
35+
})
36+
37+
err := workflow.ExecuteChildWorkflow(childCtx, ChildDomainWorkflow, CrossDomainData{Value: "test"}).Get(ctx, nil)
38+
if err != nil {
39+
logger.Error("Child workflow failed", zap.Error(err))
40+
return err
41+
}
42+
43+
logger.Info("CrossDomainWorkflow completed")
44+
return nil
45+
}
46+
47+
// ChildDomainWorkflow runs in the child domain.
48+
func ChildDomainWorkflow(ctx workflow.Context, data CrossDomainData) error {
49+
logger := workflow.GetLogger(ctx)
50+
logger.Info("ChildDomainWorkflow started", zap.String("value", data.Value))
51+
52+
ao := workflow.ActivityOptions{
53+
ScheduleToStartTimeout: time.Minute,
54+
StartToCloseTimeout: time.Minute,
55+
}
56+
ctx = workflow.WithActivityOptions(ctx, ao)
57+
58+
err := workflow.ExecuteActivity(ctx, ChildDomainActivity).Get(ctx, nil)
59+
if err != nil {
60+
return err
61+
}
62+
63+
logger.Info("ChildDomainWorkflow completed")
64+
return nil
65+
}
66+
67+
// ChildDomainActivity runs in the child domain.
68+
func ChildDomainActivity(ctx context.Context) (string, error) {
69+
logger := activity.GetLogger(ctx)
70+
logger.Info("ChildDomainActivity running")
71+
return "Hello from child domain!", nil
72+
}
73+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!-- THIS IS A GENERATED FILE -->
2+
<!-- PLEASE DO NOT EDIT -->
3+
4+
# Sample Generator
5+
6+
This folder is NOT part of the actual sample. It exists only for contributors who work on this sample. Please disregard it if you are trying to learn about Cadence.
7+
8+
To create a better learning experience for Cadence users, each sample folder is designed to be self contained. Users can view every part of writing and running workflows, including:
9+
10+
* Cadence client initialization
11+
* Worker with workflow and activity registrations
12+
* Workflow starter
13+
* and the workflow code itself
14+
15+
Some samples may have more or fewer parts depending on what they need to demonstrate.
16+
17+
In most cases, the workflow code (e.g. `workflow.go`) is the part that users care about. The rest is boilerplate needed to run that workflow. For each sample folder, the workflow code should be written by hand. The boilerplate can be generated. Keeping all parts inside one folder gives early learners more value because they can see everything together rather than jumping across directories.
18+
19+
## Contributing
20+
21+
* When creating a new sample, follow the steps mentioned in the README file in the main samples folder.
22+
* To update the sample workflow code, edit the workflow file directly.
23+
* To update the worker, client, or other boilerplate logic, edit the generator file. If your change applies to all samples, update the common generator file inside the `template` folder. Edit the generator file in this folder only when the change should affect this sample alone.

0 commit comments

Comments
 (0)