-
Notifications
You must be signed in to change notification settings - Fork 217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix panic on starting child workflows with duplicate IDs #999
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -164,6 +164,12 @@ type ( | |
stateMachineIllegalStatePanic struct { | ||
message string | ||
} | ||
|
||
// Error returned when a child workflow with the same id already exists and hasn't completed | ||
// and been removed from internal state. | ||
childWorkflowExistsWithId struct { | ||
id string | ||
} | ||
) | ||
|
||
const ( | ||
|
@@ -1253,10 +1259,18 @@ func (h *commandsHelper) recordMutableSideEffectMarker(mutableSideEffectID strin | |
return command | ||
} | ||
|
||
func (h *commandsHelper) startChildWorkflowExecution(attributes *commandpb.StartChildWorkflowExecutionCommandAttributes) commandStateMachine { | ||
// startChildWorkflowExecution can return an error in the event that there is already a child wf | ||
// with the same ID which exists as a command in memory. Other SDKs actually will send this command | ||
// to server, and have it reject it - but here the command ID is exactly equal to the child's wf ID, | ||
// and changing that without potentially blowing up backwards compatability is difficult. So we | ||
// return the error eagerly locally, which is at least an improvement on panicking. | ||
func (h *commandsHelper) startChildWorkflowExecution(attributes *commandpb.StartChildWorkflowExecutionCommandAttributes) (commandStateMachine, error) { | ||
command := h.newChildWorkflowCommandStateMachine(attributes) | ||
if h.commands[command.getID()] != nil { | ||
return nil, &childWorkflowExistsWithId{id: attributes.WorkflowId} | ||
Comment on lines
+1269
to
+1270
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't say I love this, but as is often the case in the Go SDK it seemed like the least offensive of some unfortunate options. |
||
} | ||
h.addCommand(command) | ||
return command | ||
return command, nil | ||
} | ||
|
||
func (h *commandsHelper) handleStartChildWorkflowExecutionInitiated(workflowID string) { | ||
|
@@ -1513,3 +1527,7 @@ func (h *commandsHelper) isCancelExternalWorkflowEventForChildWorkflow(cancellat | |
// will have a client generated sequence ID | ||
return len(cancellationID) == 0 | ||
} | ||
|
||
func (e *childWorkflowExistsWithId) Error() string { | ||
return fmt.Sprintf("child workflow already exists with id: %v", e.id) | ||
} |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -520,6 +520,29 @@ func (w *Workflows) ChildWorkflowCancelUnusualTransitionsRepro(ctx workflow.Cont | |||
return nil | ||||
} | ||||
|
||||
func (w *Workflows) ChildWorkflowDuplicatePanicRepro(ctx workflow.Context) error { | ||||
cwo := workflow.ChildWorkflowOptions{ | ||||
WorkflowID: "ABC-SIMPLE-CHILD-WORKFLOW-ID", | ||||
} | ||||
childCtx := workflow.WithChildOptions(ctx, cwo) | ||||
|
||||
child1 := workflow.ExecuteChildWorkflow(childCtx, w.childWorkflowWaitOnSignal) | ||||
var childWE workflow.Execution | ||||
err := child1.GetChildWorkflowExecution().Get(ctx, &childWE) | ||||
if err != nil { | ||||
return err | ||||
} | ||||
workflow.SignalExternalWorkflow(ctx, childWE.ID, childWE.RunID, "unblock", nil) | ||||
if err != nil { | ||||
return err | ||||
} | ||||
err = workflow.ExecuteChildWorkflow(childCtx, w.childWorkflowWaitOnSignal).Get(ctx, nil) | ||||
if _, ok := err.(*internal.ChildWorkflowExecutionAlreadyStartedError); !ok { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't we expose this error outside of internal? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, so, it is one of the many things that's aliased. I can't refer to the Line 135 in 8ab62d9
I don't track the whole aliasing stuff so if I need to somehow return it differently I'm all ears. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How come other errors can be used from temporal https://github.com/temporalio/sdk-go/blob/master/test/workflow_test.go#L220 but this one can't? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hahah, derp. I didn't realize this comment was on the test file. Fixed. |
||||
panic("Second child must fail to start as duplicate") | ||||
} | ||||
return nil | ||||
} | ||||
|
||||
func (w *Workflows) ActivityCancelRepro(ctx workflow.Context) ([]string, error) { | ||||
ctx, cancelFunc := workflow.WithCancel(ctx) | ||||
|
||||
|
@@ -1860,7 +1883,7 @@ func (w *Workflows) HeartbeatSpecificCount(ctx workflow.Context, interval time.D | |||
func (w *Workflows) UpsertMemo(ctx workflow.Context, memo map[string]interface{}) (*commonpb.Memo, error) { | ||||
err := workflow.UpsertMemo(ctx, memo) | ||||
if err != nil { | ||||
return nil, err; | ||||
return nil, err | ||||
} | ||||
return workflow.GetInfo(ctx).Memo, nil | ||||
} | ||||
|
@@ -1897,6 +1920,7 @@ func (w *Workflows) register(worker worker.Worker) { | |||
worker.RegisterWorkflow(w.ChildWorkflowSuccessWithParentClosePolicyTerminate) | ||||
worker.RegisterWorkflow(w.ChildWorkflowSuccessWithParentClosePolicyAbandon) | ||||
worker.RegisterWorkflow(w.ChildWorkflowCancelUnusualTransitionsRepro) | ||||
worker.RegisterWorkflow(w.ChildWorkflowDuplicatePanicRepro) | ||||
worker.RegisterWorkflow(w.ConsistentQueryWorkflow) | ||||
worker.RegisterWorkflow(w.ContextPropagator) | ||||
worker.RegisterWorkflow(w.ContinueAsNew) | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why create a new error instead of using
ChildWorkflowExecutionAlreadyStartedError
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a proto - so it doesn't impl the
Error
interface and feels icky to use in this place