Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ PROGS = helloworld \
pageflow \
signalcounter \
sideeffect \
sleep \

TEST_ARG ?= -race -v -timeout 5m
BUILD := ./build
Expand Down Expand Up @@ -68,6 +69,7 @@ TEST_DIRS=./cmd/samples/cron \
./cmd/samples/recipes/searchattributes \
./cmd/samples/recipes/sideeffect \
./cmd/samples/recipes/signalcounter \
./cmd/samples/recipes/sleep \
./cmd/samples/recovery \
./cmd/samples/pso \

Expand All @@ -81,6 +83,9 @@ helloworld:
delaystart:
go build -o bin/delaystart cmd/samples/recipes/delaystart/*.go

sleep:
go build -o bin/sleep cmd/samples/recipes/sleep/*.go

branch:
go build -o bin/branch cmd/samples/recipes/branch/*.go

Expand Down Expand Up @@ -207,6 +212,7 @@ bins: helloworld \
pageflow \
signalcounter \
sideeffect \
sleep \

test: bins
@rm -f test
Expand Down
62 changes: 62 additions & 0 deletions cmd/samples/recipes/sleep/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Sleep Workflow Sample

This sample workflow demonstrates how to use the `workflow.Sleep` function in Cadence workflows. The sleep functionality allows workflows to pause execution for a specified duration before continuing with subsequent activities.

## Sample Description

The sample workflow:
- Takes a sleep duration as input parameter
- Uses `workflow.Sleep` to pause workflow execution for the specified duration
- Executes a main activity after the sleep period completes
- Demonstrates proper error handling for sleep operations
- Shows how to configure activity options for post-sleep activities

The workflow is useful for scenarios where you need to:
- Implement delays or timeouts in workflow logic
- Wait for external events or conditions
- Implement retry mechanisms with exponential backoff
- Create scheduled or periodic workflows

## Key Components

- **Workflow**: `sleepWorkflow` demonstrates the sleep functionality with activity execution
- **Activity**: `mainSleepActivity` is executed after the sleep period
- **Sleep Duration**: Configurable duration (default: 30 seconds) passed as workflow input
- **Test**: Includes unit tests to verify sleep and activity execution

## Steps to Run Sample

1. You need a cadence service running. See details in cmd/samples/README.md

2. Run the following command to start the worker:
```
./bin/sleep -m worker
```

3. Run the following command to execute the workflow:
```
./bin/sleep -m trigger
```

You should see logs showing:
- Workflow start with sleep duration
- Sleep completion message
- Main activity execution
- Workflow completion

## Customization

To modify the sleep behavior:
- Change the `sleepDuration` in `main.go` to adjust the default sleep time
- Modify the activity options to configure timeouts for post-sleep activities
- Add additional activities or logic after the sleep period
- Implement conditional sleep based on workflow state

## Use Cases

This pattern is useful for:
- **Scheduled Tasks**: Implement workflows that need to wait before processing
- **Rate Limiting**: Add delays between API calls or external service interactions
- **Retry Logic**: Implement exponential backoff for failed operations
- **Event-Driven Workflows**: Wait for specific time periods before checking conditions
- **Batch Processing**: Add delays between batch operations to avoid overwhelming systems
62 changes: 62 additions & 0 deletions cmd/samples/recipes/sleep/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package main

import (
"flag"
"time"

"github.com/pborman/uuid"
"go.uber.org/cadence/client"
"go.uber.org/cadence/worker"

"github.com/uber-common/cadence-samples/cmd/samples/common"
)

const (
ApplicationName = "sleepTaskList"
SleepWorkflowName = "sleepWorkflow"
)

func startWorkers(h *common.SampleHelper) {
workerOptions := worker.Options{
MetricsScope: h.WorkerMetricScope,
Logger: h.Logger,
FeatureFlags: client.FeatureFlags{
WorkflowExecutionAlreadyCompletedErrorEnabled: true,
},
}
h.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)
}

func startWorkflow(h *common.SampleHelper) {
sleepDuration := 30 * time.Second
workflowOptions := client.StartWorkflowOptions{
ID: "sleep_" + uuid.New(),
TaskList: ApplicationName,
ExecutionStartToCloseTimeout: time.Minute,
DecisionTaskStartToCloseTimeout: time.Minute,
}
h.StartWorkflow(workflowOptions, SleepWorkflowName, sleepDuration)
}

func registerWorkflowAndActivity(h *common.SampleHelper) {
h.RegisterWorkflowWithAlias(sleepWorkflow, SleepWorkflowName)
h.RegisterActivity(mainSleepActivity)
}

func main() {
var mode string
flag.StringVar(&mode, "m", "trigger", "Mode is worker or trigger.")
flag.Parse()

var h common.SampleHelper
h.SetupServiceConfig()

switch mode {
case "worker":
registerWorkflowAndActivity(&h)
startWorkers(&h)
select {}
case "trigger":
startWorkflow(&h)
}
}
52 changes: 52 additions & 0 deletions cmd/samples/recipes/sleep/sleep_workflow.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package main

import (
"context"
"time"

"go.uber.org/cadence/activity"
"go.uber.org/cadence/workflow"
"go.uber.org/zap"
)

const sleepWorkflowName = "sleepWorkflow"

// sleepWorkflow demonstrates workflow.Sleep followed by a main activity call
func sleepWorkflow(ctx workflow.Context, sleepDuration time.Duration) error {
logger := workflow.GetLogger(ctx)
logger.Info("Workflow started, will sleep", zap.String("duration", sleepDuration.String()))

// Sleep for the specified duration
err := workflow.Sleep(ctx, sleepDuration)
if err != nil {
logger.Error("Sleep failed", zap.Error(err))
return err
}

logger.Info("Sleep finished, executing main activity")

// Set activity options
activityOptions := workflow.ActivityOptions{
ScheduleToStartTimeout: time.Minute,
StartToCloseTimeout: time.Minute,
HeartbeatTimeout: time.Second * 20,
}
ctx = workflow.WithActivityOptions(ctx, activityOptions)

var result string
err = workflow.ExecuteActivity(ctx, mainSleepActivity).Get(ctx, &result)
if err != nil {
logger.Error("Main activity failed", zap.Error(err))
return err
}

logger.Info("Workflow completed", zap.String("Result", result))
return nil
}

// mainSleepActivity is a simple activity for demonstration
func mainSleepActivity(ctx context.Context) (string, error) {
logger := activity.GetLogger(ctx)
logger.Info("mainSleepActivity executed")
return "Main sleep activity completed", nil
}
31 changes: 31 additions & 0 deletions cmd/samples/recipes/sleep/sleep_workflow_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package main

import (
"testing"
"time"

"github.com/stretchr/testify/require"
"go.uber.org/cadence/activity"
"go.uber.org/cadence/encoded"
"go.uber.org/cadence/testsuite"
)

func Test_Sleep(t *testing.T) {
testSuite := &testsuite.WorkflowTestSuite{}

env := testSuite.NewTestWorkflowEnvironment()
env.RegisterWorkflow(sleepWorkflow)
env.RegisterActivity(mainSleepActivity)

var activityMessage string
env.SetOnActivityCompletedListener(func(activityInfo *activity.Info, result encoded.Value, err error) {
result.Get(&activityMessage)
})

sleepDuration := 5 * time.Second
env.ExecuteWorkflow(sleepWorkflow, sleepDuration)

require.True(t, env.IsWorkflowCompleted())
require.NoError(t, env.GetWorkflowError())
require.Equal(t, "Main sleep activity completed", activityMessage)
}
Loading