go.uber.org/cadence@v1.2.9/internal/workflow_shadower_worker.go (about) 1 // Copyright (c) 2017-2021 Uber Technologies Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package internal 22 23 import ( 24 "context" 25 26 "github.com/opentracing/opentracing-go" 27 "github.com/pborman/uuid" 28 "go.uber.org/zap" 29 30 "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient" 31 "go.uber.org/cadence/.gen/go/shadower" 32 "go.uber.org/cadence/.gen/go/shared" 33 "go.uber.org/cadence/internal/common" 34 "go.uber.org/cadence/internal/common/backoff" 35 ) 36 37 type ( 38 shadowWorker struct { 39 activityWorker *activityWorker 40 41 service workflowserviceclient.Interface 42 domain string 43 taskList string 44 options ShadowOptions 45 logger *zap.Logger 46 featureFlags FeatureFlags 47 } 48 ) 49 50 func newShadowWorker( 51 service workflowserviceclient.Interface, 52 domain string, 53 shadowOptions ShadowOptions, 54 params workerExecutionParameters, 55 registry *registry, 56 ) *shadowWorker { 57 registry.RegisterActivityWithOptions(scanWorkflowActivity, RegisterActivityOptions{ 58 Name: shadower.ScanWorkflowActivityName, 59 }) 60 registry.RegisterActivityWithOptions(replayWorkflowActivity, RegisterActivityOptions{ 61 Name: shadower.ReplayWorkflowActivityName, 62 EnableAutoHeartbeat: true, 63 }) 64 65 replayer := NewWorkflowReplayerWithOptions(ReplayOptions{ 66 DataConverter: params.DataConverter, 67 ContextPropagators: params.ContextPropagators, 68 WorkflowInterceptorChainFactories: params.WorkflowInterceptorChainFactories, 69 Tracer: params.Tracer, 70 FeatureFlags: params.FeatureFlags, 71 }) 72 replayer.registry = registry 73 74 ensureRequiredParams(¶ms) 75 if len(params.TaskList) != 0 { 76 // include domain name in tasklist to avoid confliction 77 // since all shadow workflow will be run in a single system domain 78 params.TaskList = generateShadowTaskList(domain, params.TaskList) 79 params.MetricsScope = tagScope(params.MetricsScope, tagTaskList, params.TaskList) 80 params.Logger = params.Logger.With(zap.String(tagTaskList, params.TaskList)) 81 } 82 83 params.UserContext = context.WithValue(params.UserContext, serviceClientContextKey, service) 84 params.UserContext = context.WithValue(params.UserContext, workflowReplayerContextKey, replayer) 85 86 // data converter, interceptors, context propagators, tracers provided by user is for replay 87 // for the actual shadowing workflow use default values. 88 // this is required for data converter, for the other three, it should be ok to still use 89 // the value provided by user. 90 params.DataConverter = getDefaultDataConverter() 91 params.WorkflowInterceptorChainFactories = []WorkflowInterceptorFactory{} 92 params.ContextPropagators = []ContextPropagator{} 93 params.Tracer = opentracing.NoopTracer{} 94 95 activityWorker := newActivityWorker( 96 service, 97 shadower.LocalDomainName, // note: this is the system domain for all shadow workflows 98 params, 99 nil, 100 registry, 101 nil, 102 ) 103 return &shadowWorker{ 104 activityWorker: activityWorker, 105 106 service: service, 107 domain: domain, 108 taskList: params.TaskList, 109 options: shadowOptions, 110 logger: params.Logger, 111 featureFlags: params.FeatureFlags, 112 } 113 } 114 115 func (sw *shadowWorker) Start() error { 116 if err := sw.options.validateAndPopulateFields(); err != nil { 117 return err 118 } 119 120 if err := verifyDomainExist(sw.service, sw.domain, sw.logger, sw.featureFlags); err != nil { 121 return err 122 } 123 124 if len(sw.taskList) == 0 { 125 return errTaskListNotSet 126 } 127 128 if err := sw.startShadowWorkflow(); err != nil { 129 return err 130 } 131 132 return sw.activityWorker.Start() 133 } 134 135 func (sw *shadowWorker) Stop() { 136 sw.activityWorker.Stop() 137 } 138 139 func (sw *shadowWorker) startShadowWorkflow() error { 140 workflowParams := shadower.WorkflowParams{ 141 Domain: common.StringPtr(sw.domain), 142 TaskList: common.StringPtr(sw.taskList), 143 WorkflowQuery: common.StringPtr(sw.options.WorkflowQuery), 144 SamplingRate: common.Float64Ptr(sw.options.SamplingRate), 145 ShadowMode: sw.options.Mode.toThriftPtr(), 146 ExitCondition: sw.options.ExitCondition.toThriftPtr(), 147 Concurrency: common.Int32Ptr(int32(sw.options.Concurrency)), 148 } 149 150 ctx := context.Background() 151 152 workflowType, input, err := getValidatedWorkflowFunction(shadower.WorkflowName, []interface{}{workflowParams}, getDefaultDataConverter(), nil) 153 if err != nil { 154 return err 155 } 156 157 startWorkflowRequest := &shared.StartWorkflowExecutionRequest{ 158 Domain: common.StringPtr(shadower.LocalDomainName), 159 WorkflowId: common.StringPtr(sw.domain + shadower.WorkflowIDSuffix), 160 WorkflowType: workflowTypePtr(*workflowType), 161 TaskList: &shared.TaskList{ 162 Name: common.StringPtr(shadower.TaskList), 163 }, 164 Input: input, 165 ExecutionStartToCloseTimeoutSeconds: common.Int32Ptr(864000), 166 TaskStartToCloseTimeoutSeconds: common.Int32Ptr(60), 167 RequestId: common.StringPtr(uuid.New()), 168 WorkflowIdReusePolicy: shared.WorkflowIdReusePolicyAllowDuplicate.Ptr(), 169 } 170 171 startWorkflowOp := func() error { 172 tchCtx, cancel, opt := newChannelContext(ctx, sw.featureFlags) 173 defer cancel() 174 _, err := sw.service.StartWorkflowExecution(tchCtx, startWorkflowRequest, opt...) 175 if err != nil { 176 if _, ok := err.(*shared.WorkflowExecutionAlreadyStartedError); ok { 177 return nil 178 } 179 } 180 181 return err 182 } 183 184 return backoff.Retry(ctx, startWorkflowOp, createDynamicServiceRetryPolicy(ctx), isServiceTransientError) 185 } 186 187 func generateShadowTaskList(domain, taskList string) string { 188 return domain + "-" + taskList 189 }