Skip to content

Commit 4117f96

Browse files
StartWorkflowExecution: validate RequestID before calling history (cadence-workflow#5359)
What changed? Validating RequestID (uuid) at handler level. Why? When calling StartWorkflowExecution and passing wrongly formatted UUID, a generic error will be returned from persistence layer (cassandra for example). This error is not treated as non-retryable, so Cadence will try to insert wrong data multiple times. On the client side, only request-timeout will be returned which reveals no details about the nature for this failure. This change will validate UUID on handler side and no calls to history/persistence will be made. Additionally, user will get information on what data is missing or malformed. How did you test it? Unit test updated to include malformed UUID check
1 parent 1202a2e commit 4117f96

File tree

2 files changed

+39
-35
lines changed

2 files changed

+39
-35
lines changed

service/frontend/workflowHandler.go

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import (
2828
"sync/atomic"
2929
"time"
3030

31-
"github.com/pborman/uuid"
31+
"github.com/google/uuid"
3232
"go.uber.org/yarpc"
3333
"go.uber.org/yarpc/yarpcerrors"
3434
"golang.org/x/sync/errgroup"
@@ -131,7 +131,6 @@ var (
131131
errQueryTypeNotSet = &types.BadRequestError{Message: "QueryType is not set on request."}
132132
errRequestNotSet = &types.BadRequestError{Message: "Request is nil."}
133133
errNoPermission = &types.BadRequestError{Message: "No permission to do this operation."}
134-
errRequestIDNotSet = &types.BadRequestError{Message: "RequestId is not set on request."}
135134
errWorkflowTypeNotSet = &types.BadRequestError{Message: "WorkflowType is not set on request."}
136135
errInvalidRetention = &types.BadRequestError{Message: "RetentionDays is invalid."}
137136
errInvalidExecutionStartToCloseTimeoutSeconds = &types.BadRequestError{Message: "A valid ExecutionStartToCloseTimeoutSeconds is not set on request."}
@@ -615,7 +614,7 @@ func (wh *WorkflowHandler) PollForActivityTask(
615614
); err != nil {
616615
return &types.PollForActivityTaskResponse{}, nil
617616
}
618-
pollerID := uuid.New()
617+
pollerID := uuid.New().String()
619618
op := func() error {
620619
resp, err = wh.GetMatchingClient().PollForActivityTask(ctx, &types.MatchingPollForActivityTaskRequest{
621620
DomainUUID: domainID,
@@ -745,7 +744,7 @@ func (wh *WorkflowHandler) PollForDecisionTask(
745744
return &types.PollForDecisionTaskResponse{}, nil
746745
}
747746

748-
pollerID := uuid.New()
747+
pollerID := uuid.New().String()
749748
var matchingResp *types.MatchingPollForDecisionTaskResponse
750749
op := func() error {
751750
matchingResp, err = wh.GetMatchingClient().PollForDecisionTask(ctx, &types.MatchingPollForDecisionTaskRequest{
@@ -2082,18 +2081,14 @@ func (wh *WorkflowHandler) StartWorkflowExecution(
20822081
scope, sw := wh.startRequestProfileWithDomain(ctx, metrics.FrontendStartWorkflowExecutionScope, startRequest)
20832082
defer sw.Stop()
20842083

2085-
if wh.isShuttingDown() {
2086-
return nil, errShuttingDown
2087-
}
2088-
2089-
if err := wh.versionChecker.ClientSupported(ctx, wh.config.EnableClientVersionCheck()); err != nil {
2090-
return nil, wh.error(err, scope)
2091-
}
2092-
20932084
if startRequest == nil {
20942085
return nil, wh.error(errRequestNotSet, scope)
20952086
}
20962087

2088+
if wh.isShuttingDown() {
2089+
return nil, errShuttingDown
2090+
}
2091+
20972092
domainName := startRequest.GetDomain()
20982093
wfExecution := &types.WorkflowExecution{
20992094
WorkflowID: startRequest.GetWorkflowID(),
@@ -2104,6 +2099,21 @@ func (wh *WorkflowHandler) StartWorkflowExecution(
21042099
return nil, wh.error(errDomainNotSet, scope, tags...)
21052100
}
21062101

2102+
if startRequest.GetWorkflowID() == "" {
2103+
return nil, wh.error(errWorkflowIDNotSet, scope, tags...)
2104+
}
2105+
2106+
if _, err := uuid.Parse(startRequest.RequestID); err != nil {
2107+
return nil, wh.error(&types.BadRequestError{Message: fmt.Sprintf("requestId %q is not a valid UUID", startRequest.RequestID)}, scope, tags...)
2108+
}
2109+
if startRequest.WorkflowType == nil || startRequest.WorkflowType.GetName() == "" {
2110+
return nil, wh.error(errWorkflowTypeNotSet, scope, tags...)
2111+
}
2112+
2113+
if err := wh.versionChecker.ClientSupported(ctx, wh.config.EnableClientVersionCheck()); err != nil {
2114+
return nil, wh.error(err, scope)
2115+
}
2116+
21072117
if ok := wh.allow(ratelimitTypeUser, startRequest); !ok {
21082118
return nil, wh.error(createServiceBusyError(), scope, tags...)
21092119
}
@@ -2121,10 +2131,6 @@ func (wh *WorkflowHandler) StartWorkflowExecution(
21212131
return nil, wh.error(errDomainTooLong, scope, tags...)
21222132
}
21232133

2124-
if startRequest.GetWorkflowID() == "" {
2125-
return nil, wh.error(errWorkflowIDNotSet, scope, tags...)
2126-
}
2127-
21282134
if !common.ValidIDLength(
21292135
startRequest.GetWorkflowID(),
21302136
scope,
@@ -2141,20 +2147,10 @@ func (wh *WorkflowHandler) StartWorkflowExecution(
21412147
return nil, wh.error(err, scope, tags...)
21422148
}
21432149

2144-
if startRequest.GetCronSchedule() != "" {
2145-
if _, err := backoff.ValidateSchedule(startRequest.GetCronSchedule()); err != nil {
2146-
return nil, wh.error(err, scope, tags...)
2147-
}
2148-
}
2149-
21502150
wh.GetLogger().Debug(
21512151
"Received StartWorkflowExecution. WorkflowID",
21522152
tag.WorkflowID(startRequest.GetWorkflowID()))
21532153

2154-
if startRequest.WorkflowType == nil || startRequest.WorkflowType.GetName() == "" {
2155-
return nil, wh.error(errWorkflowTypeNotSet, scope, tags...)
2156-
}
2157-
21582154
if !common.ValidIDLength(
21592155
startRequest.WorkflowType.GetName(),
21602156
scope,
@@ -2189,6 +2185,11 @@ func (wh *WorkflowHandler) StartWorkflowExecution(
21892185

21902186
jitter := startRequest.GetJitterStartSeconds()
21912187
cron := startRequest.GetCronSchedule()
2188+
if cron != "" {
2189+
if _, err := backoff.ValidateSchedule(startRequest.GetCronSchedule()); err != nil {
2190+
return nil, wh.error(err, scope, tags...)
2191+
}
2192+
}
21922193
if jitter > 0 && cron != "" {
21932194
// Calculate the cron duration and ensure that jitter is not greater than the cron duration,
21942195
// because that would be confusing to users.
@@ -2205,10 +2206,6 @@ func (wh *WorkflowHandler) StartWorkflowExecution(
22052206
}
22062207
}
22072208

2208-
if startRequest.GetRequestID() == "" {
2209-
return nil, wh.error(errRequestIDNotSet, scope, tags...)
2210-
}
2211-
22122209
if !common.ValidIDLength(
22132210
startRequest.GetRequestID(),
22142211
scope,
@@ -4264,8 +4261,10 @@ func validateExecution(w *types.WorkflowExecution) error {
42644261
if w.GetWorkflowID() == "" {
42654262
return errWorkflowIDNotSet
42664263
}
4267-
if w.GetRunID() != "" && uuid.Parse(w.GetRunID()) == nil {
4268-
return errInvalidRunID
4264+
if w.GetRunID() != "" {
4265+
if _, err := uuid.Parse(w.GetRunID()); err != nil {
4266+
return errInvalidRunID
4267+
}
42694268
}
42704269
return nil
42714270
}
@@ -4715,7 +4714,7 @@ func (wh *WorkflowHandler) normalizeVersionedErrors(ctx context.Context, err err
47154714
func constructRestartWorkflowRequest(w *types.WorkflowExecutionStartedEventAttributes, domain string, identity string, workflowID string) *types.StartWorkflowExecutionRequest {
47164715

47174716
startRequest := &types.StartWorkflowExecutionRequest{
4718-
RequestID: uuid.New(),
4717+
RequestID: uuid.New().String(),
47194718
Domain: domain,
47204719
WorkflowID: workflowID,
47214720
WorkflowType: &types.WorkflowType{

service/frontend/workflowHandler_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,12 @@ func (s *workflowHandlerSuite) TestStartWorkflowExecution_Failed_RequestIdNotSet
323323
}
324324
_, err := wh.StartWorkflowExecution(context.Background(), startWorkflowExecutionRequest)
325325
s.Error(err)
326-
s.Equal(errRequestIDNotSet, err)
326+
s.Equal(&types.BadRequestError{Message: "requestId \"\" is not a valid UUID"}, err)
327+
startWorkflowExecutionRequest.RequestID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
328+
_, err = wh.StartWorkflowExecution(context.Background(), startWorkflowExecutionRequest)
329+
s.Error(err)
330+
s.Equal(&types.BadRequestError{Message: "requestId \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\" is not a valid UUID"}, err)
331+
327332
}
328333

329334
func (s *workflowHandlerSuite) TestStartWorkflowExecution_Failed_BadDelayStartSeconds() {
@@ -1389,7 +1394,7 @@ func (s *workflowHandlerSuite) TestRestartWorkflowExecution__Success() {
13891394
},
13901395
Identity: "",
13911396
})
1392-
s.Equal(resp.GetRunID(), testRunID)
1397+
s.Equal(testRunID, resp.GetRunID())
13931398
s.NoError(err)
13941399
}
13951400

0 commit comments

Comments
 (0)