diff --git a/cmd/samples/recipes/branch/README.md b/cmd/samples/recipes/branch/README.md new file mode 100644 index 00000000..ec568c33 --- /dev/null +++ b/cmd/samples/recipes/branch/README.md @@ -0,0 +1,108 @@ +# Branch Sample + +This sample demonstrates **parallel execution** - running multiple activities concurrently using two different approaches. + +> **Looking for a visual guide?** See [new_samples/hello_world](../../../../new_samples/hello_world/) for a step-by-step tutorial with screenshots. + +## Two Approaches to Parallelism + +### 1. Futures (`-c branch`) +Execute activities in parallel, collect futures, then wait for all: + +``` + ┌─────────────┐ + │ Start │ + └──────┬──────┘ + ┌────────┼────────┐ + ▼ ▼ ▼ +┌─────┐ ┌─────┐ ┌─────┐ +│ A1 │ │ A2 │ │ A3 │ (parallel) +└──┬──┘ └──┬──┘ └──┬──┘ + └────────┼────────┘ + ▼ + ┌─────────────┐ + │ Wait all │ + └─────────────┘ +``` + +### 2. Coroutines (`workflow.Go`) +Spawn goroutine-like coroutines with channels for coordination: + +``` + ┌─────────────┐ + │ Start │ + └──────┬──────┘ + ┌───┴───┐ + ▼ ▼ + ┌─────────┐ ┌─────┐ + │ A1 → A2 │ │ A3 │ (parallel branches) + └────┬────┘ └──┬──┘ + └────┬────┘ + ▼ + ┌─────────────┐ + │ channel.Receive │ + └─────────────┘ +``` + +## Prerequisites + +1. Cadence server running (see [main README](../../../../README.md)) +2. Build the samples: `make` + +## Running the Sample + +```bash +# Terminal 1: Start worker +./bin/branch -m worker + +# Terminal 2: Trigger workflows +./bin/branch -m trigger # Coroutines approach (default) +./bin/branch -m trigger -c branch # Futures approach +``` + +## Key Code + +### Futures Pattern +```go +var futures []workflow.Future +for i := 1; i <= totalBranches; i++ { + future := workflow.ExecuteActivity(ctx, sampleActivity, input) + futures = append(futures, future) +} +// Wait for all +for _, future := range futures { + future.Get(ctx, nil) +} +``` + +### Coroutines Pattern +```go +waitChannel := workflow.NewChannel(ctx) + +workflow.Go(ctx, func(ctx workflow.Context) { + workflow.ExecuteActivity(ctx, activity1).Get(ctx, nil) + workflow.ExecuteActivity(ctx, activity2).Get(ctx, nil) + waitChannel.Send(ctx, "done") +}) + +workflow.Go(ctx, func(ctx workflow.Context) { + workflow.ExecuteActivity(ctx, activity3).Get(ctx, nil) + waitChannel.Send(ctx, "done") +}) + +// Wait for both coroutines +waitChannel.Receive(ctx, nil) +waitChannel.Receive(ctx, nil) +``` + +## Testing + +```bash +go test -v ./cmd/samples/recipes/branch/ +``` + +## References + +- [Parallel Execution](https://cadenceworkflow.io/docs/go-client/execute-activity/) +- [Workflow Coroutines](https://cadenceworkflow.io/docs/go-client/workflow-patterns/) + diff --git a/cmd/samples/recipes/cancelactivity/README.md b/cmd/samples/recipes/cancelactivity/README.md new file mode 100644 index 00000000..d9417ed6 --- /dev/null +++ b/cmd/samples/recipes/cancelactivity/README.md @@ -0,0 +1,60 @@ +# Cancel Activity Sample + +This sample demonstrates **graceful activity cancellation** with cleanup operations. + +> **Looking for a visual guide?** See [new_samples/hello_world](../../../../new_samples/hello_world/) for screenshots. + +## How It Works + +``` +┌──────────────────┐ +│ Start Workflow │ +└────────┬─────────┘ + ▼ +┌──────────────────┐ ┌──────────────────┐ +│ activityToBe │────▶│ User cancels │ +│ Canceled() │ │ workflow │ +│ (heartbeating) │ └────────┬─────────┘ +└──────────────────┘ │ + │◀───────────────────────┘ + ▼ +┌──────────────────┐ +│ cleanupActivity()│ (runs via NewDisconnectedContext) +└──────────────────┘ +``` + +## Prerequisites + +1. Cadence server running (see [main README](../../../../README.md)) +2. Build the samples: `make` + +## Running the Sample + +```bash +# Terminal 1: Start worker +./bin/cancelactivity -m worker + +# Terminal 2: Trigger workflow +./bin/cancelactivity -m trigger + +# Terminal 3: Cancel the workflow (copy WorkflowID from trigger output) +./bin/cancelactivity -m cancel -w +``` + +## Key Code + +```go +defer func() { + if cadence.IsCanceledError(retError) { + newCtx, _ := workflow.NewDisconnectedContext(ctx) + workflow.ExecuteActivity(newCtx, cleanupActivity).Get(ctx, nil) + } +}() +``` + +## Testing + +```bash +go test -v ./cmd/samples/recipes/cancelactivity/ +``` + diff --git a/cmd/samples/recipes/childworkflow/README.md b/cmd/samples/recipes/childworkflow/README.md new file mode 100644 index 00000000..c2413bc1 --- /dev/null +++ b/cmd/samples/recipes/childworkflow/README.md @@ -0,0 +1,89 @@ +# Child Workflow Sample + +This sample demonstrates **parent-child workflow relationships** and the **ContinueAsNew** pattern for long-running workflows. + +> **Looking for a visual guide?** See [new_samples/hello_world](../../../../new_samples/hello_world/) for a step-by-step tutorial with screenshots. + +## How It Works + +``` +┌─────────────────────────────────────────────────────┐ +│ Parent Workflow │ +│ ExecuteChildWorkflow(child, 0, 5) │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────┐ │ +│ │ Child Workflow │ │ +│ │ Run 1 → ContinueAsNew │ │ +│ │ Run 2 → ContinueAsNew │ │ +│ │ Run 3 → ContinueAsNew │ │ +│ │ Run 4 → ContinueAsNew │ │ +│ │ Run 5 → Complete ✓ │ │ +│ └─────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ Parent receives: "completed after 5 runs" │ +└─────────────────────────────────────────────────────┘ +``` + +**Use cases:** +- Breaking large workflows into modular pieces +- Long-running workflows that need to reset history (ContinueAsNew) +- Workflow decomposition for better organization + +## Prerequisites + +1. Cadence server running (see [main README](../../../../README.md)) +2. Build the samples: `make` + +## Running the Sample + +```bash +# Terminal 1: Start worker +./bin/childworkflow -m worker + +# Terminal 2: Trigger workflow +./bin/childworkflow -m trigger +``` + +## Key Code + +### Parent Workflow +```go +cwo := workflow.ChildWorkflowOptions{ + WorkflowID: childID, + ExecutionStartToCloseTimeout: time.Minute, +} +ctx = workflow.WithChildOptions(ctx, cwo) + +var result string +err := workflow.ExecuteChildWorkflow(ctx, sampleChildWorkflow, 0, 5).Get(ctx, &result) +``` + +### Child Workflow with ContinueAsNew +```go +func sampleChildWorkflow(ctx workflow.Context, totalCount, runCount int) (string, error) { + totalCount++ + runCount-- + + if runCount == 0 { + return fmt.Sprintf("completed after %v runs", totalCount), nil + } + + // Restart workflow with new parameters (resets history) + return "", workflow.NewContinueAsNewError(ctx, sampleChildWorkflow, totalCount, runCount) +} +``` + +## Why ContinueAsNew? + +Workflow history grows with each event. For long-running workflows, use `ContinueAsNew` to: +- Reset the history size +- Prevent hitting history limits +- Keep workflows performant + +## References + +- [Child Workflows](https://cadenceworkflow.io/docs/go-client/child-workflows/) +- [ContinueAsNew](https://cadenceworkflow.io/docs/go-client/continue-as-new/) + diff --git a/cmd/samples/recipes/choice/README.md b/cmd/samples/recipes/choice/README.md new file mode 100644 index 00000000..142b6e8f --- /dev/null +++ b/cmd/samples/recipes/choice/README.md @@ -0,0 +1,105 @@ +# Choice Sample + +This sample demonstrates **conditional workflow logic** - executing different activities based on runtime decisions. + +> **Looking for a visual guide?** See [new_samples/hello_world](../../../../new_samples/hello_world/) for a step-by-step tutorial with screenshots. + +## Two Patterns + +### 1. Exclusive Choice (`-c single`) +Execute ONE activity based on a decision: + +``` +┌───────────────┐ +│ getOrder() │ +│ returns "apple"│ +└───────┬───────┘ + │ + ┌────┴────┬────────┬────────┐ + ▼ ▼ ▼ ▼ +┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ +│apple │ │banana│ │cherry│ │orange│ +│ ✓ │ │ │ │ │ │ │ +└──────┘ └──────┘ └──────┘ └──────┘ +``` + +### 2. Multi Choice (`-c multi`) +Execute MULTIPLE activities in parallel based on a basket: + +``` +┌───────────────────┐ +│ getBasketOrder() │ +│ returns ["apple", │ +│ "cherry","orange"]│ +└─────────┬─────────┘ + ┌─────┼─────┐ + ▼ ▼ ▼ +┌──────┐┌──────┐┌──────┐ +│apple ││cherry││orange│ (parallel) +│ ✓ ││ ✓ ││ ✓ │ +└──────┘└──────┘└──────┘ +``` + +## Prerequisites + +1. Cadence server running (see [main README](../../../../README.md)) +2. Build the samples: `make` + +## Running the Sample + +```bash +# Terminal 1: Start worker +./bin/choice -m worker + +# Terminal 2: Trigger workflows +./bin/choice -m trigger -c single # Exclusive choice (default) +./bin/choice -m trigger -c multi # Multi choice +``` + +## Key Code + +### Exclusive Choice +```go +var orderChoice string +workflow.ExecuteActivity(ctx, getOrderActivity).Get(ctx, &orderChoice) + +switch orderChoice { +case "apple": + workflow.ExecuteActivity(ctx, orderAppleActivity, orderChoice) +case "banana": + workflow.ExecuteActivity(ctx, orderBananaActivity, orderChoice) +// ... +} +``` + +### Multi Choice +```go +var choices []string +workflow.ExecuteActivity(ctx, getBasketOrderActivity).Get(ctx, &choices) + +var futures []workflow.Future +for _, item := range choices { + switch item { + case "apple": + f = workflow.ExecuteActivity(ctx, orderAppleActivity, item) + // ... + } + futures = append(futures, f) +} + +// Wait for all +for _, future := range futures { + future.Get(ctx, nil) +} +``` + +## Testing + +```bash +go test -v ./cmd/samples/recipes/choice/ +``` + +## References + +- [Cadence Documentation](https://cadenceworkflow.io) + diff --git a/cmd/samples/recipes/crossdomain/README.md b/cmd/samples/recipes/crossdomain/README.md new file mode 100644 index 00000000..b9b47d40 --- /dev/null +++ b/cmd/samples/recipes/crossdomain/README.md @@ -0,0 +1,47 @@ +# Cross Domain Sample + +This sample demonstrates **cross-domain child workflows** - executing child workflows in different Cadence domains. + +> **Looking for a visual guide?** See [new_samples/hello_world](../../../../new_samples/hello_world/) for screenshots. + +## How It Works + +``` +┌─────────────────────────────────────────┐ +│ Parent Workflow (domain0) │ +│ │ │ +│ ┌───────────────┴───────────────┐ │ +│ ▼ ▼ │ +│ ┌─────────┐ ┌─────────┐ │ +│ │ Child │ │ Child │ │ +│ │ domain1 │ │ domain2 │ │ +│ │(cluster1)│ │(cluster0)│ │ +│ └─────────┘ └─────────┘ │ +└─────────────────────────────────────────┘ +``` + +**Use case:** Multi-tenant systems, cross-region workflows, domain isolation. + +## Prerequisites + +1. Cadence server with multiple domains configured +2. Build the samples: `make` + +## Running the Sample + +```bash +./bin/crossdomain +``` + +**Note:** This sample requires specific multi-domain setup. See main.go for domain configuration. + +## Key Code + +```go +ctx1 := workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{ + Domain: domain1, + TaskList: tasklist1, +}) +workflow.ExecuteChildWorkflow(ctx1, wf1, data).Get(ctx1, nil) +``` + diff --git a/cmd/samples/recipes/delaystart/README.md b/cmd/samples/recipes/delaystart/README.md new file mode 100644 index 00000000..c308ee39 --- /dev/null +++ b/cmd/samples/recipes/delaystart/README.md @@ -0,0 +1,55 @@ +# Delay Start Sample + +This sample demonstrates **delayed workflow execution** - starting a workflow that waits before executing its logic. + +> **Looking for a visual guide?** See [new_samples/hello_world](../../../../new_samples/hello_world/) for screenshots. + +## How It Works + +``` +┌──────────────────┐ +│ Workflow Start │ +│ (with delay) │ +└────────┬─────────┘ + │ + ⏳ Wait 10s + │ + ▼ +┌──────────────────┐ +│ delayStartActivity│ +│ executes │ +└──────────────────┘ +``` + +**Use case:** Scheduled tasks, delayed notifications, batch processing at specific times. + +## Prerequisites + +1. Cadence server running (see [main README](../../../../README.md)) +2. Build the samples: `make` + +## Running the Sample + +```bash +# Terminal 1: Start worker +./bin/delaystart -m worker + +# Terminal 2: Trigger workflow (will execute after delay) +./bin/delaystart -m trigger +``` + +## Key Code + +```go +func delayStartWorkflow(ctx workflow.Context, delayStart time.Duration) error { + logger.Info("workflow started after waiting for " + delayStart.String()) + workflow.ExecuteActivity(ctx, delayStartActivity, delayStart).Get(ctx, &result) +} +``` + +## Testing + +```bash +go test -v ./cmd/samples/recipes/delaystart/ +``` + diff --git a/cmd/samples/recipes/dynamic/README.md b/cmd/samples/recipes/dynamic/README.md new file mode 100644 index 00000000..0ed726d8 --- /dev/null +++ b/cmd/samples/recipes/dynamic/README.md @@ -0,0 +1,52 @@ +# Dynamic Sample + +This sample demonstrates **dynamic activity invocation** - calling activities by string name instead of function reference. + +> **Looking for a visual guide?** See [new_samples/hello_world](../../../../new_samples/hello_world/) for screenshots. + +## How It Works + +``` +┌──────────────────────────────────────────────┐ +│ Workflow │ +│ │ +│ ExecuteActivity(ctx, "main.getGreeting") │ +│ ExecuteActivity(ctx, "main.getName") │ +│ ExecuteActivity(ctx, "main.sayGreeting") │ +│ │ +│ (activities called by STRING name) │ +└──────────────────────────────────────────────┘ +``` + +**Use case:** Plugin systems, configuration-driven workflows, runtime activity selection. + +## Prerequisites + +1. Cadence server running (see [main README](../../../../README.md)) +2. Build the samples: `make` + +## Running the Sample + +```bash +# Terminal 1: Start worker +./bin/dynamic -m worker + +# Terminal 2: Trigger workflow +./bin/dynamic -m trigger +``` + +## Key Code + +```go +const getGreetingActivityName = "main.getGreetingActivity" + +// Call activity by name string instead of function +workflow.ExecuteActivity(ctx, getGreetingActivityName).Get(ctx, &result) +``` + +## Testing + +```bash +go test -v ./cmd/samples/recipes/dynamic/ +``` + diff --git a/cmd/samples/recipes/greetings/README.md b/cmd/samples/recipes/greetings/README.md new file mode 100644 index 00000000..54eb4201 --- /dev/null +++ b/cmd/samples/recipes/greetings/README.md @@ -0,0 +1,85 @@ +# Greetings Sample + +This sample demonstrates **sequential activity execution** - running multiple activities one after another, passing results between them. + +> **Looking for a visual guide?** See [new_samples/hello_world](../../../../new_samples/hello_world/) for a step-by-step tutorial with screenshots. + + +## How It Works + +The workflow executes 3 activities in sequence: + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ +│ getGreeting() │───▶│ getName() │───▶│ sayGreeting(g, n) │ +│ returns "Hello" │ │ returns "Cadence"│ │ returns "Hello │ +└─────────────────┘ └─────────────────┘ │ Cadence!" │ + └─────────────────────┘ +``` + +## Prerequisites + +1. Cadence server running (see [main README](../../../../README.md)) +2. Build the samples: + ```bash + make + ``` + +## Running the Sample + +### Step 1: Start the Worker + +```bash +./bin/greetings -m worker +``` + +### Step 2: Trigger the Workflow + +```bash +./bin/greetings -m trigger +``` + +### Step 3: View the Result + +Check the worker terminal: +``` +Workflow completed. {"Result": "Greeting: Hello Cadence!\n"} +``` + +Or view in the Cadence Web UI at [localhost:8088](http://localhost:8088). + +## Key Code + +### Sequential Execution Pattern + +```go +// Activity 1: Get greeting +var greetResult string +err := workflow.ExecuteActivity(ctx, getGreetingActivity).Get(ctx, &greetResult) + +// Activity 2: Get name +var nameResult string +err = workflow.ExecuteActivity(ctx, getNameActivity).Get(ctx, &nameResult) + +// Activity 3: Combine results +var sayResult string +err = workflow.ExecuteActivity(ctx, sayGreetingActivity, greetResult, nameResult).Get(ctx, &sayResult) +``` + +Each `ExecuteActivity().Get()` blocks until the activity completes, ensuring sequential execution. + +## Testing + +```bash +# Unit tests +go test -v ./cmd/samples/recipes/greetings/ -run TestUnitTestSuite + +# Replay tests +go test -v ./cmd/samples/recipes/greetings/ -run TestReplayWorkflowHistoryFromFile +``` + +## References + +- [Cadence Documentation](https://cadenceworkflow.io) +- [Activity Basics](https://cadenceworkflow.io/docs/concepts/activities/) + diff --git a/cmd/samples/recipes/helloworld/README.md b/cmd/samples/recipes/helloworld/README.md new file mode 100644 index 00000000..c1fdbd87 --- /dev/null +++ b/cmd/samples/recipes/helloworld/README.md @@ -0,0 +1,139 @@ +# Hello World Sample + +This is the foundational Cadence workflow sample demonstrating basic workflow and activity execution. + +> **Looking for a step-by-step visual guide?** See the [new_samples/hello_world](../../../../new_samples/hello_world/) tutorial with screenshots and detailed setup instructions. + +## Prerequisites + +1. Cadence server running (see [main README](../../../../README.md)) +2. Build the samples: + ```bash + make + ``` + +## Running the Sample + +### Step 1: Start the Worker + +```bash +./bin/helloworld -m worker +``` + +The worker registers the workflow and activity, then polls for tasks. You should see: +``` +Started Worker. {"worker": "helloWorldGroup"} +``` + +### Step 2: Trigger the Workflow + +In a new terminal: +```bash +./bin/helloworld -m trigger +``` + +This starts a new workflow execution. You should see: +``` +Started Workflow {"WorkflowID": "helloworld_", "RunID": ""} +``` + +### Step 3: View the Result + +Check the worker terminal - you'll see the workflow complete: +``` +helloworld workflow started +helloworld activity started +Workflow completed. {"Result": "Hello Cadence!"} +``` + +Or view in the Cadence Web UI at [localhost:8088](http://localhost:8088). + +## Worker Modes + +This sample supports three modes via the `-m` flag: + +### `worker` - Normal execution +```bash +./bin/helloworld -m worker +``` +Starts a worker that processes workflow and activity tasks. + +### `trigger` - Start workflow +```bash +./bin/helloworld -m trigger +``` +Starts a new workflow execution (requires a running worker). + +### `shadower` - Shadow testing +```bash +./bin/helloworld -m shadower +``` +Replays completed workflows to verify code changes are deterministic. Useful for validating workflow modifications before deployment. + +## Testing + +### Unit Tests +```bash +go test -v ./cmd/samples/recipes/helloworld/ -run Test_Workflow +``` + +### Replay Tests +```bash +go test -v ./cmd/samples/recipes/helloworld/ -run TestReplayWorkflowHistoryFromFile +``` + +Replay tests use `helloworld.json` (a recorded workflow history) to verify that workflow code produces the same decisions. This catches non-deterministic changes. + +### Shadow Tests +```bash +go test -v ./cmd/samples/recipes/helloworld/ -run TestWorkflowShadowing +``` + +Shadow tests replay recent production workflows against your local code to detect breaking changes. + +## Understanding the Code + +### Workflow Definition (`helloworld_workflow.go`) + +```go +func helloWorldWorkflow(ctx workflow.Context, name string) error { + // Configure activity options + ao := workflow.ActivityOptions{ + ScheduleToStartTimeout: time.Minute, + StartToCloseTimeout: time.Minute, + HeartbeatTimeout: time.Second * 20, + } + ctx = workflow.WithActivityOptions(ctx, ao) + + // Execute the activity + var result string + err := workflow.ExecuteActivity(ctx, helloWorldActivity, name).Get(ctx, &result) + // ... +} +``` + +### Non-Determinism Warning + +The workflow code contains commented-out code demonstrating a **non-deterministic change**: + +```go +// Un-commenting the following code will cause replay tests to fail +// because it changes the workflow's decision history +// err := workflow.ExecuteActivity(ctx, helloWorldActivity, name).Get(ctx, &helloworldResult) +``` + +This teaches an important lesson: adding/removing activities changes the workflow's behavior and breaks running workflows. Use [workflow versioning](https://cadenceworkflow.io/docs/go-client/workflow-versioning/) for safe changes. + +## Configuration + +This sample uses `config/development.yaml` for connection settings: +- Domain name +- Cadence server host/port +- Metrics configuration + +## References + +- [Cadence Documentation](https://cadenceworkflow.io) +- [Workflow Versioning](https://cadenceworkflow.io/docs/go-client/workflow-versioning/) +- [Testing Workflows](https://cadenceworkflow.io/docs/go-client/workflow-testing/) + diff --git a/cmd/samples/recipes/pickfirst/README.md b/cmd/samples/recipes/pickfirst/README.md new file mode 100644 index 00000000..b3dd10cf --- /dev/null +++ b/cmd/samples/recipes/pickfirst/README.md @@ -0,0 +1,61 @@ +# Pick First Sample + +This sample demonstrates **racing activities** - running multiple activities in parallel and using the first result. + +> **Looking for a visual guide?** See [new_samples/hello_world](../../../../new_samples/hello_world/) for screenshots. + +## How It Works + +``` + ┌─────────────────┐ + │ Start │ + └────────┬────────┘ + ┌─────┴─────┐ + ▼ ▼ + ┌─────────┐ ┌─────────┐ + │ Task 1 │ │ Task 2 │ (parallel) + │ 2 sec │ │ 10 sec │ + └────┬────┘ └────┬────┘ + │ │ + First ✓ Cancel ✗ + │ + ▼ + ┌─────────┐ + │ Result │ + └─────────┘ +``` + +**Use case:** Failover, load balancing, fastest-response-wins patterns. + +## Prerequisites + +1. Cadence server running (see [main README](../../../../README.md)) +2. Build the samples: `make` + +## Running the Sample + +```bash +# Terminal 1: Start worker +./bin/pickfirst -m worker + +# Terminal 2: Trigger workflow +./bin/pickfirst -m trigger +``` + +## Key Code + +```go +f1 := workflow.ExecuteActivity(childCtx, sampleActivity, 0, time.Second*2) +f2 := workflow.ExecuteActivity(childCtx, sampleActivity, 1, time.Second*10) + +selector.AddFuture(f1, handler).AddFuture(f2, handler) +selector.Select(ctx) // Wait for first +cancelHandler() // Cancel others +``` + +## Testing + +```bash +go test -v ./cmd/samples/recipes/pickfirst/ +``` + diff --git a/cmd/samples/recipes/retryactivity/README.md b/cmd/samples/recipes/retryactivity/README.md new file mode 100644 index 00000000..14521845 --- /dev/null +++ b/cmd/samples/recipes/retryactivity/README.md @@ -0,0 +1,63 @@ +# Retry Activity Sample + +This sample demonstrates **activity retry with heartbeat progress** - automatic retries with resume from last checkpoint. + +> **Looking for a visual guide?** See [new_samples/hello_world](../../../../new_samples/hello_world/) for screenshots. + +## How It Works + +``` +┌────────────────────────────────────────────────┐ +│ Batch Processing (20 items) │ +│ │ +│ Attempt 1: Process 1-6 → FAIL (simulated) │ +│ Heartbeat: item 6 ✓ │ +│ │ +│ Attempt 2: Resume from 7 → Process 7-12 → FAIL│ +│ Heartbeat: item 12 ✓ │ +│ │ +│ Attempt 3: Resume from 13 → Process 13-20 ✓ │ +└────────────────────────────────────────────────┘ +``` + +**Use case:** Batch processing, unreliable external APIs, resumable long-running tasks. + +## Prerequisites + +1. Cadence server running (see [main README](../../../../README.md)) +2. Build the samples: `make` + +## Running the Sample + +```bash +# Terminal 1: Start worker +./bin/retryactivity -m worker + +# Terminal 2: Trigger workflow +./bin/retryactivity -m trigger +``` + +## Key Code + +```go +RetryPolicy: &cadence.RetryPolicy{ + InitialInterval: time.Second, + BackoffCoefficient: 2.0, + MaximumAttempts: 5, +} + +// Resume from heartbeat progress +if activity.HasHeartbeatDetails(ctx) { + activity.GetHeartbeatDetails(ctx, &completedIdx) + i = completedIdx + 1 // Resume from last checkpoint +} + +activity.RecordHeartbeat(ctx, i) // Save progress +``` + +## Testing + +```bash +go test -v ./cmd/samples/recipes/retryactivity/ +``` + diff --git a/cmd/samples/recipes/sideeffect/README.md b/cmd/samples/recipes/sideeffect/README.md new file mode 100644 index 00000000..11fc0d3e --- /dev/null +++ b/cmd/samples/recipes/sideeffect/README.md @@ -0,0 +1,53 @@ +# Side Effect Sample + +This sample demonstrates **workflow.SideEffect** - handling non-deterministic operations safely. + +> **Looking for a visual guide?** See [new_samples/hello_world](../../../../new_samples/hello_world/) for screenshots. + +## How It Works + +``` +┌─────────────────────────────────────────────────┐ +│ Workflow │ +│ │ +│ SideEffect(func() { │ +│ return uuid.New() // Non-deterministic! │ +│ }) │ +│ │ +│ First execution: Generates UUID, stores it │ +│ Replay: Returns stored UUID (no regeneration) │ +└─────────────────────────────────────────────────┘ +``` + +**Use case:** UUID generation, random numbers, timestamps, external state queries. + +## Why SideEffect? + +Workflow code must be deterministic for replay. `SideEffect` captures non-deterministic values once and returns the same value on replay. + +## Prerequisites + +1. Cadence server running (see [main README](../../../../README.md)) +2. Build the samples: `make` + +## Running the Sample + +```bash +./bin/sideeffect +``` + +**Note:** This sample starts worker, triggers workflow, and queries result in one command. + +## Key Code + +```go +var value string +workflow.SideEffect(ctx, func(ctx workflow.Context) interface{} { + return uuid.New().String() // Only executed once, stored for replay +}).Get(&value) +``` + +## References + +- [Side Effects](https://cadenceworkflow.io/docs/go-client/side-effect/) + diff --git a/cmd/samples/recipes/signalcounter/README.md b/cmd/samples/recipes/signalcounter/README.md new file mode 100644 index 00000000..f0085ff2 --- /dev/null +++ b/cmd/samples/recipes/signalcounter/README.md @@ -0,0 +1,68 @@ +# Signal Counter Sample + +This sample demonstrates **signal handling with ContinueAsNew** - processing unlimited signals by resetting history. + +> **Looking for a visual guide?** See [new_samples/hello_world](../../../../new_samples/hello_world/) for screenshots. + +## How It Works + +``` +┌─────────────────────────────────────────────────┐ +│ Workflow (counter = 0) │ +│ │ +│ Signal channelA: +5 → counter = 5 │ +│ Signal channelB: +3 → counter = 8 │ +│ Signal channelA: +2 → counter = 10 │ +│ │ +│ (maxSignalsPerExecution reached) │ +│ │ │ +│ ▼ │ +│ ContinueAsNew(counter = 10) │ +│ │ │ +│ ▼ │ +│ New Workflow (counter = 10) │ +│ ... continues receiving signals ... │ +└─────────────────────────────────────────────────┘ +``` + +**Use case:** Event aggregation, counters, long-running signal processors. + +## Prerequisites + +1. Cadence server running (see [main README](../../../../README.md)) +2. Build the samples: `make` + +## Running the Sample + +```bash +# Terminal 1: Start worker +./bin/signalcounter -m worker + +# Terminal 2: Trigger workflow +./bin/signalcounter -m trigger + +# Terminal 3: Send signals (copy WorkflowID) +./bin/signalcounter -m signal -w -n channelA -i 5 +./bin/signalcounter -m signal -w -n channelB -i 3 +``` + +## Key Code + +```go +for { + selector.AddReceive(workflow.GetSignalChannel(ctx, "channelA"), handler) + selector.AddReceive(workflow.GetSignalChannel(ctx, "channelB"), handler) + + if signalsPerExecution >= maxSignalsPerExecution { + return workflow.NewContinueAsNewError(ctx, workflow, counter) + } + selector.Select(ctx) +} +``` + +## Testing + +```bash +go test -v ./cmd/samples/recipes/signalcounter/ +``` + diff --git a/cmd/samples/recipes/splitmerge/README.md b/cmd/samples/recipes/splitmerge/README.md new file mode 100644 index 00000000..9d6b19b8 --- /dev/null +++ b/cmd/samples/recipes/splitmerge/README.md @@ -0,0 +1,68 @@ +# Split Merge Sample + +This sample demonstrates **divide and conquer** - splitting work into chunks, processing in parallel, then merging results. + +> **Looking for a visual guide?** See [new_samples/hello_world](../../../../new_samples/hello_world/) for screenshots. + +## How It Works + +``` + ┌─────────────────┐ + │ Split Work │ + │ (N chunks) │ + └────────┬────────┘ + ┌─────────────┼─────────────┐ + ▼ ▼ ▼ + ┌──────────┐ ┌──────────┐ ┌──────────┐ + │ Chunk 1 │ │ Chunk 2 │ │ Chunk 3 │ (parallel) + └────┬─────┘ └────┬─────┘ └────┬─────┘ + └─────────────┼─────────────┘ + ▼ + ┌─────────────────┐ + │ Merge Results │ + │ {count, sum} │ + └─────────────────┘ +``` + +**Use case:** Large file processing, batch analytics, ETL pipelines, map-reduce patterns. + +## Prerequisites + +1. Cadence server running (see [main README](../../../../README.md)) +2. Build the samples: `make` + +## Running the Sample + +```bash +# Terminal 1: Start worker +./bin/splitmerge -m worker + +# Terminal 2: Trigger workflow +./bin/splitmerge -m trigger +``` + +## Key Code + +```go +chunkResultChannel := workflow.NewChannel(ctx) + +for i := 1; i <= workerCount; i++ { + workflow.Go(ctx, func(ctx workflow.Context) { + workflow.ExecuteActivity(ctx, chunkProcessingActivity, chunkID).Get(ctx, &result) + chunkResultChannel.Send(ctx, result) + }) +} + +// Merge results +for i := 1; i <= workerCount; i++ { + chunkResultChannel.Receive(ctx, &result) + totalSum += result.SumInChunk +} +``` + +## Testing + +```bash +go test -v ./cmd/samples/recipes/splitmerge/ +``` + diff --git a/cmd/samples/recipes/timer/README.md b/cmd/samples/recipes/timer/README.md new file mode 100644 index 00000000..eeebf6e2 --- /dev/null +++ b/cmd/samples/recipes/timer/README.md @@ -0,0 +1,84 @@ +# Timer Sample + +This sample demonstrates **timer-based notifications** - running a long operation with a timeout that triggers an alert if processing takes too long. + +> **Looking for a visual guide?** See [new_samples/hello_world](../../../../new_samples/hello_world/) for a step-by-step tutorial with screenshots. + +## How It Works + +``` +┌──────────────────────────────────────────────────────────────┐ +│ Workflow Start │ +└──────────────────────────────────────────────────────────────┘ + │ + ┌───────────────┴───────────────┐ + ▼ ▼ + ┌─────────────────────┐ ┌─────────────────────┐ + │ orderProcessing() │ │ Timer (3 seconds) │ + │ (random 0-10 sec) │ │ │ + └─────────────────────┘ └─────────────────────┘ + │ │ + │ If timer fires first: │ + │ ◀───────────────────────────── + │ sendEmail() │ + │ │ + ▼ │ + ┌─────────────────────┐ │ + │ Wait for processing │◀───────────────────┘ + │ to complete │ + └─────────────────────┘ +``` + +**Use case:** Order processing with SLA monitoring - notify customer if processing is delayed, but don't cancel the operation. + +## Prerequisites + +1. Cadence server running (see [main README](../../../../README.md)) +2. Build the samples: `make` + +## Running the Sample + +```bash +# Terminal 1: Start worker +./bin/timer -m worker + +# Terminal 2: Trigger workflow +./bin/timer -m trigger +``` + +**Possible outcomes:** +- Processing finishes in < 3 seconds → Timer cancelled, no email +- Processing takes > 3 seconds → Email sent, then wait for completion + +## Key Code + +```go +// Start long-running activity +f := workflow.ExecuteActivity(ctx, orderProcessingActivity) +selector.AddFuture(f, func(f workflow.Future) { + processingDone = true + cancelHandler() // Cancel timer if processing finishes first +}) + +// Start timer for notification threshold +timerFuture := workflow.NewTimer(childCtx, processingTimeThreshold) +selector.AddFuture(timerFuture, func(f workflow.Future) { + if !processingDone { + workflow.ExecuteActivity(ctx, sendEmailActivity) // Send notification + } +}) + +selector.Select(ctx) // Wait for either to complete +``` + +## Testing + +```bash +go test -v ./cmd/samples/recipes/timer/ +``` + +## References + +- [Cadence Timers](https://cadenceworkflow.io/docs/go-client/timers/) +- [Workflow Selectors](https://cadenceworkflow.io/docs/go-client/selectors/) + diff --git a/cmd/samples/recipes/tracing/README.md b/cmd/samples/recipes/tracing/README.md new file mode 100644 index 00000000..f7ff5d8b --- /dev/null +++ b/cmd/samples/recipes/tracing/README.md @@ -0,0 +1,57 @@ +# Tracing Sample + +This sample demonstrates **distributed tracing integration** - adding observability to workflows with OpenTracing/Jaeger. + +> **Looking for a visual guide?** See [new_samples/hello_world](../../../../new_samples/hello_world/) for screenshots. + +## How It Works + +``` +┌─────────────────────────────────────────────────┐ +│ Jaeger / OpenTracing │ +│ │ +│ Trace: helloWorldWorkflow │ +│ └── Span: ExecuteActivity │ +│ └── Span: helloWorldActivity │ +│ │ +│ (traces propagate across workflow/activity) │ +└─────────────────────────────────────────────────┘ +``` + +**Use case:** Performance monitoring, debugging, APM integration, observability. + +## Prerequisites + +1. Cadence server running (see [main README](../../../../README.md)) +2. Jaeger or compatible tracing backend +3. Build the samples: `make` + +## Running the Sample + +```bash +# Terminal 1: Start worker (with tracing configured) +./bin/tracing -m worker + +# Terminal 2: Trigger workflow +./bin/tracing -m trigger +``` + +View traces in Jaeger UI (typically http://localhost:16686). + +## Key Code + +```go +// Configure tracer in SampleHelper +h.Tracer = opentracing.GlobalTracer() + +// Traces automatically propagate through: +// - Workflow execution +// - Activity execution +// - Child workflows +``` + +## References + +- [Cadence Tracing](https://cadenceworkflow.io/docs/go-client/tracing/) +- [OpenTracing](https://opentracing.io/) +