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(&params)
    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  }