From 6fce826f80bdeaf6a0635fc76bafbf737300d14c Mon Sep 17 00:00:00 2001 From: Manu Srivastava Date: Wed, 9 Sep 2020 22:34:03 -0700 Subject: [PATCH 1/2] initial checkin --- service/history/handler.go | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/service/history/handler.go b/service/history/handler.go index a5818bc874d..589be19036b 100644 --- a/service/history/handler.go +++ b/service/history/handler.go @@ -982,12 +982,35 @@ func (h *Handler) SignalWithStartWorkflowExecution(ctx context.Context, request return nil, h.error(err1, scope, namespaceID, workflowID) } - resp, err2 := engine.SignalWithStartWorkflowExecution(ctx, request) - if err2 != nil { - return nil, h.error(err2, scope, namespaceID, workflowID) - } + for { + resp, err2 := engine.SignalWithStartWorkflowExecution(ctx, request) + if err2 == nil { + return resp, nil + } - return resp, nil + // Two simultaneous SignalWithStart requests might try to start a workflow at the same time. + // This can result in one of the requests failing with one of two possible errors: + // 1) If it is a brand new WF ID, one of the requests can fail with WorkflowExecutionAlreadyStartedError + // (createMode is persistence.CreateWorkflowModeBrandNew) + // 2) If it an already existing WF ID, one of the requests can fail with a CurrentWorkflowConditionFailedError + // (createMode is persisetence.CreateWorkflowModeWorkflowIDReuse) + // If either error occurs, just go ahead and retry. It should succeed on the subsequent attempt. + // For simplicity, we keep trying unless the context finishes or we get an error that is not one of the + // two mentioned above. + _, isExecutionAlreadyStartedErr := err2.(*persistence.WorkflowExecutionAlreadyStartedError) + _, isWorkflowConditionFailedErr := err2.(*persistence.CurrentWorkflowConditionFailedError) + + isContextDone := false + select { + case <-ctx.Done(): + isContextDone = true + default: + } + + if (!isExecutionAlreadyStartedErr && !isWorkflowConditionFailedErr) || isContextDone { + return nil, h.error(err2, scope, namespaceID, workflowID) + } + } } // RemoveSignalMutableState is used to remove a signal request ID that was previously recorded. This is currently From b7cb3520ce9b2236f0670a3aa8d6e75b2625b4f7 Mon Sep 17 00:00:00 2001 From: Manu Srivastava Date: Thu, 10 Sep 2020 10:27:19 -0700 Subject: [PATCH 2/2] address CR --- service/history/handler.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/service/history/handler.go b/service/history/handler.go index 589be19036b..3fc231d4610 100644 --- a/service/history/handler.go +++ b/service/history/handler.go @@ -1004,6 +1004,9 @@ func (h *Handler) SignalWithStartWorkflowExecution(ctx context.Context, request select { case <-ctx.Done(): isContextDone = true + if ctxErr := ctx.Err(); ctxErr != nil { + err2 = ctxErr + } default: }