diff --git a/sleep-for-days/README.md b/sleep-for-days/README.md new file mode 100644 index 00000000..0d6969aa --- /dev/null +++ b/sleep-for-days/README.md @@ -0,0 +1,16 @@ +### Sleep for days + +This sample demonstrates how to create a Temporal workflow that runs forever, sending an email every 30 days. + +### Steps to run this sample: +1) Run a [Temporal service](https://github.com/temporalio/samples-go/tree/main/#how-to-use). +2) Run the following command to start the worker +``` +go run worker/main.go +``` +3) Run the following command to start the example +``` +go run starter/main.go +``` + +This sample will run indefinitely until you send a `complete` signal to the workflow. See how to send a signal via Temporal CLI [here](https://docs.temporal.io/cli/workflow#signal). \ No newline at end of file diff --git a/sleep-for-days/sleepfordays_workflow.go b/sleep-for-days/sleepfordays_workflow.go new file mode 100644 index 00000000..1e44256f --- /dev/null +++ b/sleep-for-days/sleepfordays_workflow.go @@ -0,0 +1,37 @@ +package sleepfordays + +import ( + "context" + "fmt" + "time" + + "go.temporal.io/sdk/activity" + "go.temporal.io/sdk/workflow" +) + +func SleepForDaysWorkflow(ctx workflow.Context) (string, error) { + ctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{ + StartToCloseTimeout: 10 * time.Second, + }) + + isComplete := false + sigChan := workflow.GetSignalChannel(ctx, "complete") + + for !isComplete { + workflow.ExecuteActivity(ctx, SendEmailActivity, "Sleeping for 30 days") + selector := workflow.NewSelector(ctx) + selector.AddFuture(workflow.NewTimer(ctx, time.Hour*24*30), func(f workflow.Future) {}) + selector.AddReceive(sigChan, func(c workflow.ReceiveChannel, more bool) { + isComplete = true + }) + selector.Select(ctx) + } + + return "done", nil +} + +// A stub Activity for sending an email. +func SendEmailActivity(ctx context.Context, msg string) error { + activity.GetLogger(ctx).Info(fmt.Sprintf(`Sending email: "%v"\n`, msg)) + return nil +} diff --git a/sleep-for-days/sleepfordays_workflow_test.go b/sleep-for-days/sleepfordays_workflow_test.go new file mode 100644 index 00000000..ee7d28e5 --- /dev/null +++ b/sleep-for-days/sleepfordays_workflow_test.go @@ -0,0 +1,39 @@ +package sleepfordays + +import ( + "testing" + "time" + + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "go.temporal.io/sdk/testsuite" +) + +func TestSleepForDaysWorkflow(t *testing.T) { + testSuite := &testsuite.WorkflowTestSuite{} + env := testSuite.NewTestWorkflowEnvironment() + + numActivityCalls := 0 + env.RegisterActivity(SendEmailActivity) + env.OnActivity(SendEmailActivity, mock.Anything, mock.Anything).Run( + func(args mock.Arguments) { numActivityCalls++ }, + ).Return(nil) + + startTime := env.Now() + + // Time-skip 90 days. + env.RegisterDelayedCallback(func() { + // Check that the activity has been called 3 times. + require.Equal(t, 3, numActivityCalls) + // Send the signal to complete the workflow. + env.SignalWorkflow("complete", nil) + // Expect no more activity calls to have been made - workflow is complete. + require.Equal(t, 3, numActivityCalls) + // Expect more than 90 days to have passed. + require.Equal(t, env.Now().Sub(startTime), time.Hour*24*90) + }, time.Hour*24*90) + + // Execute workflow. + env.ExecuteWorkflow(SleepForDaysWorkflow) +} diff --git a/sleep-for-days/starter/main.go b/sleep-for-days/starter/main.go new file mode 100644 index 00000000..bfbead7c --- /dev/null +++ b/sleep-for-days/starter/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "context" + "log" + + sleepfordays "github.com/temporalio/samples-go/sleep-for-days" + "go.temporal.io/sdk/client" +) + +func main() { + c, err := client.Dial(client.Options{}) + if err != nil { + log.Fatalln("Unable to create client", err) + } + defer c.Close() + + workflowOptions := client.StartWorkflowOptions{ + TaskQueue: "sleep-for-days", + } + + we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, sleepfordays.SleepForDaysWorkflow) + if err != nil { + log.Fatalln("Unable to execute workflow", err) + } + + log.Println("Started sleep-for-days workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) + + // Synchronously wait for the workflow completion (will run indefinitely until it receives a signal) + var result string + err = we.Get(context.Background(), &result) + if err != nil { + log.Fatalln("Unable get workflow result", err) + } + log.Println("Workflow result:", result) +} diff --git a/sleep-for-days/worker/main.go b/sleep-for-days/worker/main.go new file mode 100644 index 00000000..cef3ab5a --- /dev/null +++ b/sleep-for-days/worker/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "log" + + "go.temporal.io/sdk/client" + "go.temporal.io/sdk/worker" + + sleepfordays "github.com/temporalio/samples-go/sleep-for-days" +) + +func main() { + // The client and worker are heavyweight objects that should be created once per process. + c, err := client.Dial(client.Options{}) + if err != nil { + log.Fatalln("Unable to create client", err) + } + defer c.Close() + + w := worker.New(c, "sleep-for-days", worker.Options{}) + + w.RegisterWorkflow(sleepfordays.SleepForDaysWorkflow) + w.RegisterActivity(sleepfordays.SendEmailActivity) + + err = w.Run(worker.InterruptCh()) + if err != nil { + log.Fatalln("Unable to start worker", err) + } +}