From 9d3d76cb59da978305c5b3ee493da234f2dd71ba Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Fri, 25 Oct 2024 07:25:57 -0400 Subject: [PATCH 1/5] New Update-With-Start API --- early-return/starter/main.go | 42 +++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/early-return/starter/main.go b/early-return/starter/main.go index ad54eb5d..25f28b74 100644 --- a/early-return/starter/main.go +++ b/early-return/starter/main.go @@ -7,6 +7,7 @@ import ( "github.com/pborman/uuid" "github.com/temporalio/samples-go/early-return" + enumspb "go.temporal.io/api/enums/v1" "go.temporal.io/sdk/client" ) @@ -20,31 +21,27 @@ func main() { ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - updateOperation := client.NewUpdateWithStartWorkflowOperation( - client.UpdateWorkflowOptions{ - UpdateName: earlyreturn.UpdateName, - WaitForStage: client.WorkflowUpdateStageCompleted, - }) - tx := earlyreturn.Transaction{ID: uuid.New(), SourceAccount: "Bob", TargetAccount: "Alice", Amount: 100} - workflowOptions := client.StartWorkflowOptions{ - ID: "early-return-workflow-ID-" + tx.ID, - TaskQueue: earlyreturn.TaskQueueName, - WithStartOperation: updateOperation, - } - we, err := c.ExecuteWorkflow(ctxWithTimeout, workflowOptions, earlyreturn.Workflow, tx) + + startWorkflowOp, err := c.NewWithStartWorkflowOperation(client.StartWorkflowOptions{ + ID: "early-return-workflow-ID-" + tx.ID, + WorkflowIDConflictPolicy: enumspb.WORKFLOW_ID_CONFLICT_POLICY_FAIL, + TaskQueue: earlyreturn.TaskQueueName, + }, earlyreturn.Workflow, tx) if err != nil { - log.Fatalln("Error executing workflow:", err) + // E.g. missing conflict policy, or workflow arguments do not match workflow definition. + log.Fatalln("Error creating start workflow operation:", err) } - log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) - - updateHandle, err := updateOperation.Get(ctxWithTimeout) + updateHandle, err := c.UpdateWithStartWorkflow(ctxWithTimeout, client.UpdateWorkflowOptions{ + UpdateName: earlyreturn.UpdateName, + WaitForStage: client.WorkflowUpdateStageCompleted, + }, startWorkflowOp) if err != nil { - log.Fatalln("Error obtaining update handle:", err) + log.Fatalln("Error issuing update-with-start:", err) } - - err = updateHandle.Get(ctxWithTimeout, nil) + var earlyReturnResult any + err = updateHandle.Get(ctxWithTimeout, &earlyReturnResult) if err != nil { // The workflow will continue running, cancelling the transaction. @@ -52,6 +49,11 @@ func main() { log.Fatalln("Error obtaining update result:", err) } - log.Println("Transaction initialized successfully") + workflowRun, err := startWorkflowOp.Get(ctxWithTimeout) + if err != nil { + log.Fatalln("Error obtaining workflow run:", err) + } + log.Println("Transaction initialized successfully, with runID:", workflowRun.GetRunID()) // The workflow will continue running, completing the transaction. + } From 027c39c9cd106d4f09c43b132ed3301979f3a1f6 Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Mon, 2 Dec 2024 22:40:58 -0500 Subject: [PATCH 2/5] Do not return error from struct constructor --- early-return/starter/main.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/early-return/starter/main.go b/early-return/starter/main.go index 25f28b74..2164fafc 100644 --- a/early-return/starter/main.go +++ b/early-return/starter/main.go @@ -23,15 +23,11 @@ func main() { tx := earlyreturn.Transaction{ID: uuid.New(), SourceAccount: "Bob", TargetAccount: "Alice", Amount: 100} - startWorkflowOp, err := c.NewWithStartWorkflowOperation(client.StartWorkflowOptions{ + startWorkflowOp := c.NewWithStartWorkflowOperation(client.StartWorkflowOptions{ ID: "early-return-workflow-ID-" + tx.ID, WorkflowIDConflictPolicy: enumspb.WORKFLOW_ID_CONFLICT_POLICY_FAIL, TaskQueue: earlyreturn.TaskQueueName, }, earlyreturn.Workflow, tx) - if err != nil { - // E.g. missing conflict policy, or workflow arguments do not match workflow definition. - log.Fatalln("Error creating start workflow operation:", err) - } updateHandle, err := c.UpdateWithStartWorkflow(ctxWithTimeout, client.UpdateWorkflowOptions{ UpdateName: earlyreturn.UpdateName, From 73bccde8687f3b5b3f85e13b702215d506e59cbd Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Tue, 3 Dec 2024 19:34:07 -0500 Subject: [PATCH 3/5] Use a single struct for options --- early-return/starter/main.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/early-return/starter/main.go b/early-return/starter/main.go index 2164fafc..df22cf5d 100644 --- a/early-return/starter/main.go +++ b/early-return/starter/main.go @@ -29,10 +29,13 @@ func main() { TaskQueue: earlyreturn.TaskQueueName, }, earlyreturn.Workflow, tx) - updateHandle, err := c.UpdateWithStartWorkflow(ctxWithTimeout, client.UpdateWorkflowOptions{ - UpdateName: earlyreturn.UpdateName, - WaitForStage: client.WorkflowUpdateStageCompleted, - }, startWorkflowOp) + updateHandle, err := c.UpdateWithStartWorkflow(ctxWithTimeout, client.UpdateWithStartWorkflowOptions{ + UpdateOptions: client.UpdateWorkflowOptions{ + UpdateName: earlyreturn.UpdateName, + WaitForStage: client.WorkflowUpdateStageCompleted, + }, + StartWorkflowOperation: startWorkflowOp, + }) if err != nil { log.Fatalln("Error issuing update-with-start:", err) } From 86b35447e1f1518aab64f0286a09b8b35f9d98f9 Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Fri, 6 Dec 2024 11:40:15 -0500 Subject: [PATCH 4/5] Comments --- early-return/starter/main.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/early-return/starter/main.go b/early-return/starter/main.go index df22cf5d..f2c86d4e 100644 --- a/early-return/starter/main.go +++ b/early-return/starter/main.go @@ -24,7 +24,11 @@ func main() { tx := earlyreturn.Transaction{ID: uuid.New(), SourceAccount: "Bob", TargetAccount: "Alice", Amount: 100} startWorkflowOp := c.NewWithStartWorkflowOperation(client.StartWorkflowOptions{ - ID: "early-return-workflow-ID-" + tx.ID, + ID: "early-return-workflow-ID-" + tx.ID, + // WorkflowIDConflictPolicy is required when using + // UpdateWithStartWorkflow. Here we use FAIL, because we do not expect + // this workflow ID to exist already, and so we want an error if it + // does. WorkflowIDConflictPolicy: enumspb.WORKFLOW_ID_CONFLICT_POLICY_FAIL, TaskQueue: earlyreturn.TaskQueueName, }, earlyreturn.Workflow, tx) @@ -37,6 +41,10 @@ func main() { StartWorkflowOperation: startWorkflowOp, }) if err != nil { + // For example, a client-side validation error (e.g. missing conflict + // policy or invalid workflow argument types in the start operation), or + // a server-side failure (e.g. failed to start workflow, or exceeded + // limit on concurrent update per workflow execution). log.Fatalln("Error issuing update-with-start:", err) } var earlyReturnResult any From 87866bc875a13735c51fe13e58df7d27763f1bf3 Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Fri, 6 Dec 2024 11:41:44 -0500 Subject: [PATCH 5/5] TODO --- early-return/workflow.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/early-return/workflow.go b/early-return/workflow.go index 2ea47762..80e401d9 100644 --- a/early-return/workflow.go +++ b/early-return/workflow.go @@ -36,6 +36,8 @@ func Workflow(ctx workflow.Context, tx Transaction) error { return run(ctx, tx) } +// TODO: workflow should return final Transaction summary, and early return +// should return some sort of transaction conformation. func run(ctx workflow.Context, tx Transaction) error { logger := workflow.GetLogger(ctx)