go.uber.org/cadence@v1.2.9/internal/internal_activity.go (about)

     1  // Copyright (c) 2017-2020 Uber Technologies Inc.
     2  // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc.
     3  //
     4  // Permission is hereby granted, free of charge, to any person obtaining a copy
     5  // of this software and associated documentation files (the "Software"), to deal
     6  // in the Software without restriction, including without limitation the rights
     7  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8  // copies of the Software, and to permit persons to whom the Software is
     9  // furnished to do so, subject to the following conditions:
    10  //
    11  // The above copyright notice and this permission notice shall be included in
    12  // all copies or substantial portions of the Software.
    13  //
    14  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    20  // THE SOFTWARE.
    21  
    22  package internal
    23  
    24  // All code in this file is private to the package.
    25  
    26  import (
    27  	"context"
    28  	"errors"
    29  	"fmt"
    30  	"reflect"
    31  	"time"
    32  
    33  	"github.com/opentracing/opentracing-go"
    34  	"github.com/uber-go/tally"
    35  	"go.uber.org/zap"
    36  
    37  	"go.uber.org/cadence/.gen/go/shared"
    38  	"go.uber.org/cadence/internal/common"
    39  )
    40  
    41  type (
    42  	// activity is an interface of an activity implementation.
    43  	activity interface {
    44  		Execute(ctx context.Context, input []byte) ([]byte, error)
    45  		ActivityType() ActivityType
    46  		GetFunction() interface{}
    47  		GetOptions() RegisterActivityOptions
    48  	}
    49  
    50  	activityInfo struct {
    51  		activityID string
    52  	}
    53  
    54  	localActivityInfo struct {
    55  		activityID string
    56  	}
    57  
    58  	// activityOptions configuration parameters for scheduling an activity
    59  	activityOptions struct {
    60  		ActivityID                    *string // Users can choose IDs but our framework makes it optional to decrease the crust.
    61  		TaskListName                  string
    62  		ScheduleToCloseTimeoutSeconds int32
    63  		ScheduleToStartTimeoutSeconds int32
    64  		StartToCloseTimeoutSeconds    int32
    65  		HeartbeatTimeoutSeconds       int32
    66  		WaitForCancellation           bool
    67  		OriginalTaskListName          string
    68  		RetryPolicy                   *shared.RetryPolicy
    69  	}
    70  
    71  	localActivityOptions struct {
    72  		ScheduleToCloseTimeoutSeconds int32
    73  		RetryPolicy                   *RetryPolicy
    74  	}
    75  
    76  	executeActivityParams struct {
    77  		activityOptions
    78  		ActivityType  ActivityType
    79  		Input         []byte
    80  		DataConverter DataConverter
    81  		Header        *shared.Header
    82  	}
    83  
    84  	executeLocalActivityParams struct {
    85  		localActivityOptions
    86  		ActivityFn    interface{} // local activity function pointer
    87  		ActivityType  string      // local activity type
    88  		InputArgs     []interface{}
    89  		WorkflowInfo  *WorkflowInfo
    90  		DataConverter DataConverter
    91  		Attempt       int32
    92  		ScheduledTime time.Time
    93  		Header        *shared.Header
    94  	}
    95  
    96  	// asyncActivityClient for requesting activity execution
    97  	asyncActivityClient interface {
    98  		// The ExecuteActivity schedules an activity with a callback handler.
    99  		// If the activity failed to complete the callback error would indicate the failure
   100  		// and it can be one of ActivityTaskFailedError, ActivityTaskTimeoutError, ActivityTaskCanceledError
   101  		ExecuteActivity(parameters executeActivityParams, callback resultHandler) *activityInfo
   102  
   103  		// This only initiates cancel request for activity. if the activity is configured to not waitForCancellation then
   104  		// it would invoke the callback handler immediately with error code ActivityTaskCanceledError.
   105  		// If the activity is not running(either scheduled or started) then it is a no-operation.
   106  		RequestCancelActivity(activityID string)
   107  	}
   108  
   109  	// localActivityClient for requesting local activity execution
   110  	localActivityClient interface {
   111  		ExecuteLocalActivity(params executeLocalActivityParams, callback laResultHandler) *localActivityInfo
   112  
   113  		RequestCancelLocalActivity(activityID string)
   114  	}
   115  
   116  	activityEnvironment struct {
   117  		taskToken          []byte
   118  		workflowExecution  WorkflowExecution
   119  		activityID         string
   120  		activityType       ActivityType
   121  		serviceInvoker     ServiceInvoker
   122  		logger             *zap.Logger
   123  		metricsScope       tally.Scope
   124  		isLocalActivity    bool
   125  		heartbeatTimeout   time.Duration
   126  		deadline           time.Time
   127  		scheduledTimestamp time.Time
   128  		startedTimestamp   time.Time
   129  		taskList           string
   130  		dataConverter      DataConverter
   131  		attempt            int32 // starts from 0.
   132  		heartbeatDetails   []byte
   133  		workflowType       *WorkflowType
   134  		workflowDomain     string
   135  		workerStopChannel  <-chan struct{}
   136  		contextPropagators []ContextPropagator
   137  		tracer             opentracing.Tracer
   138  	}
   139  
   140  	// context.WithValue need this type instead of basic type string to avoid lint error
   141  	contextKey string
   142  )
   143  
   144  const (
   145  	activityEnvContextKey          contextKey = "activityEnv"
   146  	activityOptionsContextKey      contextKey = "activityOptions"
   147  	localActivityOptionsContextKey contextKey = "localActivityOptions"
   148  )
   149  
   150  func getActivityEnv(ctx context.Context) *activityEnvironment {
   151  	env := ctx.Value(activityEnvContextKey)
   152  	if env == nil {
   153  		panic("getActivityEnv: Not an activity context")
   154  	}
   155  	return env.(*activityEnvironment)
   156  }
   157  
   158  func getActivityOptions(ctx Context) *activityOptions {
   159  	eap := ctx.Value(activityOptionsContextKey)
   160  	if eap == nil {
   161  		return nil
   162  	}
   163  	return eap.(*activityOptions)
   164  }
   165  
   166  func getLocalActivityOptions(ctx Context) *localActivityOptions {
   167  	opts := ctx.Value(localActivityOptionsContextKey)
   168  	if opts == nil {
   169  		return nil
   170  	}
   171  	return opts.(*localActivityOptions)
   172  }
   173  
   174  func getValidatedActivityOptions(ctx Context) (*activityOptions, error) {
   175  	p := getActivityOptions(ctx)
   176  	if p == nil {
   177  		// We need task list as a compulsory parameter. This can be removed after registration
   178  		return nil, errActivityParamsBadRequest
   179  	}
   180  	if p.TaskListName == "" {
   181  		// We default to origin task list name.
   182  		p.TaskListName = p.OriginalTaskListName
   183  	}
   184  	if p.ScheduleToStartTimeoutSeconds <= 0 {
   185  		return nil, errors.New("missing or negative ScheduleToStartTimeoutSeconds")
   186  	}
   187  	if p.StartToCloseTimeoutSeconds <= 0 {
   188  		return nil, errors.New("missing or negative StartToCloseTimeoutSeconds")
   189  	}
   190  	if p.ScheduleToCloseTimeoutSeconds < 0 {
   191  		return nil, errors.New("invalid negative ScheduleToCloseTimeoutSeconds")
   192  	}
   193  	if p.ScheduleToCloseTimeoutSeconds == 0 {
   194  		// This is a optional parameter, we default to sum of the other two timeouts.
   195  		p.ScheduleToCloseTimeoutSeconds = p.ScheduleToStartTimeoutSeconds + p.StartToCloseTimeoutSeconds
   196  	}
   197  	if p.HeartbeatTimeoutSeconds < 0 {
   198  		return nil, errors.New("invalid negative HeartbeatTimeoutSeconds")
   199  	}
   200  	if err := validateRetryPolicy(p.RetryPolicy); err != nil {
   201  		return nil, err
   202  	}
   203  
   204  	return p, nil
   205  }
   206  
   207  func getValidatedLocalActivityOptions(ctx Context) (*localActivityOptions, error) {
   208  	p := getLocalActivityOptions(ctx)
   209  	if p == nil {
   210  		return nil, errLocalActivityParamsBadRequest
   211  	}
   212  	if p.ScheduleToCloseTimeoutSeconds <= 0 {
   213  		return nil, errors.New("missing or negative ScheduleToCloseTimeoutSeconds")
   214  	}
   215  
   216  	return p, nil
   217  }
   218  
   219  func validateRetryPolicy(p *shared.RetryPolicy) error {
   220  	if p == nil {
   221  		return nil
   222  	}
   223  
   224  	if p.GetInitialIntervalInSeconds() <= 0 {
   225  		return errors.New("missing or negative InitialIntervalInSeconds on retry policy")
   226  	}
   227  	if p.GetMaximumIntervalInSeconds() < 0 {
   228  		return errors.New("negative MaximumIntervalInSeconds on retry policy is invalid")
   229  	}
   230  	if p.GetMaximumIntervalInSeconds() == 0 {
   231  		// if not set, default to 100x of initial interval
   232  		p.MaximumIntervalInSeconds = common.Int32Ptr(100 * p.GetInitialIntervalInSeconds())
   233  	}
   234  	if p.GetMaximumAttempts() < 0 {
   235  		return errors.New("negative MaximumAttempts on retry policy is invalid")
   236  	}
   237  	if p.GetExpirationIntervalInSeconds() < 0 {
   238  		return errors.New("ExpirationIntervalInSeconds cannot be less than 0 on retry policy")
   239  	}
   240  	if p.GetBackoffCoefficient() < 1 {
   241  		return errors.New("BackoffCoefficient on retry policy cannot be less than 1.0")
   242  	}
   243  	if p.GetMaximumAttempts() == 0 && p.GetExpirationIntervalInSeconds() == 0 {
   244  		return errors.New("both MaximumAttempts and ExpirationIntervalInSeconds on retry policy are not set, at least one of them must be set")
   245  	}
   246  
   247  	return nil
   248  }
   249  
   250  func validateFunctionArgs(f interface{}, args []interface{}, isWorkflow bool) error {
   251  	fType := reflect.TypeOf(f)
   252  	if fType == nil || fType.Kind() != reflect.Func {
   253  		return fmt.Errorf("Provided type: %v is not a function type", f)
   254  	}
   255  	fnName := getFunctionName(f)
   256  
   257  	fnArgIndex := 0
   258  	// Skip Context function argument.
   259  	if fType.NumIn() > 0 {
   260  		if isWorkflow && isWorkflowContext(fType.In(0)) {
   261  			fnArgIndex++
   262  		}
   263  		if !isWorkflow && isActivityContext(fType.In(0)) {
   264  			fnArgIndex++
   265  		}
   266  	}
   267  
   268  	// Validate provided args match with function order match.
   269  	if fType.NumIn()-fnArgIndex != len(args) {
   270  		return fmt.Errorf(
   271  			"expected %d args for function: %v but found %v",
   272  			fType.NumIn()-fnArgIndex, fnName, len(args))
   273  	}
   274  
   275  	for i := 0; fnArgIndex < fType.NumIn(); fnArgIndex, i = fnArgIndex+1, i+1 {
   276  		fnArgType := fType.In(fnArgIndex)
   277  		argType := reflect.TypeOf(args[i])
   278  		if argType != nil && !argType.AssignableTo(fnArgType) {
   279  			return fmt.Errorf(
   280  				"cannot assign function argument: %d from type: %s to type: %s",
   281  				fnArgIndex+1, argType, fnArgType,
   282  			)
   283  		}
   284  	}
   285  
   286  	return nil
   287  }
   288  
   289  func getValidatedActivityFunction(f interface{}, args []interface{}, registry *registry) (*ActivityType, error) {
   290  	fnName := ""
   291  	fType := reflect.TypeOf(f)
   292  	switch getKind(fType) {
   293  	case reflect.String:
   294  		fnName = reflect.ValueOf(f).String()
   295  	case reflect.Func:
   296  		if err := validateFunctionArgs(f, args, false); err != nil {
   297  			return nil, err
   298  		}
   299  		fnName = getFunctionName(f)
   300  		if alias, ok := registry.getActivityAlias(fnName); ok {
   301  			fnName = alias
   302  		}
   303  
   304  	default:
   305  		return nil, fmt.Errorf(
   306  			"invalid type 'f' parameter provided, it can be either activity function or name of the activity: %v", f)
   307  	}
   308  
   309  	return &ActivityType{Name: fnName}, nil
   310  }
   311  
   312  func getKind(fType reflect.Type) reflect.Kind {
   313  	if fType == nil {
   314  		return reflect.Invalid
   315  	}
   316  	return fType.Kind()
   317  }
   318  
   319  func isActivityContext(inType reflect.Type) bool {
   320  	contextElem := reflect.TypeOf((*context.Context)(nil)).Elem()
   321  	return inType != nil && inType.Implements(contextElem)
   322  }
   323  
   324  func validateFunctionAndGetResults(f interface{}, values []reflect.Value, dataConverter DataConverter) ([]byte, error) {
   325  	resultSize := len(values)
   326  
   327  	if resultSize < 1 || resultSize > 2 {
   328  		fnName := getFunctionName(f)
   329  		return nil, fmt.Errorf(
   330  			"the function: %v signature returns %d results, it is expecting to return either error or (result, error)",
   331  			fnName, resultSize)
   332  	}
   333  
   334  	var result []byte
   335  	var err error
   336  
   337  	// Parse result
   338  	if resultSize > 1 {
   339  		retValue := values[0]
   340  		if retValue.Kind() != reflect.Ptr || !retValue.IsNil() {
   341  			result, err = encodeArg(dataConverter, retValue.Interface())
   342  			if err != nil {
   343  				return nil, err
   344  			}
   345  		}
   346  	}
   347  
   348  	// Parse error.
   349  	errValue := values[resultSize-1]
   350  	if errValue.IsNil() {
   351  		return result, nil
   352  	}
   353  	errInterface, ok := errValue.Interface().(error)
   354  	if !ok {
   355  		return nil, fmt.Errorf(
   356  			"Failed to parse error result as it is not of error interface: %v",
   357  			errValue)
   358  	}
   359  	return result, errInterface
   360  }
   361  
   362  func serializeResults(f interface{}, results []interface{}, dataConverter DataConverter) (result []byte, err error) {
   363  	// results contain all results including error
   364  	resultSize := len(results)
   365  
   366  	if resultSize < 1 || resultSize > 2 {
   367  		fnName := getFunctionName(f)
   368  		err = fmt.Errorf(
   369  			"the function: %v signature returns %d results, it is expecting to return either error or (result, error)",
   370  			fnName, resultSize)
   371  		return
   372  	}
   373  	if resultSize > 1 {
   374  		retValue := results[0]
   375  		if retValue != nil {
   376  			result, err = encodeArg(dataConverter, retValue)
   377  			if err != nil {
   378  				return nil, err
   379  			}
   380  		}
   381  	}
   382  	errResult := results[resultSize-1]
   383  	if errResult != nil {
   384  		var ok bool
   385  		err, ok = errResult.(error)
   386  		if !ok {
   387  			err = fmt.Errorf(
   388  				"failed to serialize error result as it is not of error interface: %v",
   389  				errResult)
   390  		}
   391  	}
   392  	return
   393  }
   394  
   395  func deSerializeFnResultFromFnType(fnType reflect.Type, result []byte, to interface{}, dataConverter DataConverter) error {
   396  	if fnType.Kind() != reflect.Func {
   397  		return fmt.Errorf("expecting only function type but got type: %v", fnType)
   398  	}
   399  
   400  	// We already validated during registration that it either have (result, error) (or) just error.
   401  	if fnType.NumOut() <= 1 {
   402  		return nil
   403  	} else if fnType.NumOut() == 2 {
   404  		if result == nil {
   405  			return nil
   406  		}
   407  		err := decodeArg(dataConverter, result, to)
   408  		if err != nil {
   409  			return err
   410  		}
   411  	}
   412  	return nil
   413  }
   414  
   415  func deSerializeFunctionResult(f interface{}, result []byte, to interface{}, dataConverter DataConverter, registry *registry) error {
   416  	fType := reflect.TypeOf(f)
   417  	if dataConverter == nil {
   418  		dataConverter = getDefaultDataConverter()
   419  	}
   420  
   421  	switch getKind(fType) {
   422  	case reflect.Func:
   423  		// We already validated that it either have (result, error) (or) just error.
   424  		return deSerializeFnResultFromFnType(fType, result, to, dataConverter)
   425  
   426  	case reflect.String:
   427  		// If we know about this function through registration then we will try to return corresponding result type.
   428  		fnName := reflect.ValueOf(f).String()
   429  		if activity, ok := registry.GetActivity(fnName); ok {
   430  			return deSerializeFnResultFromFnType(reflect.TypeOf(activity.GetFunction()), result, to, dataConverter)
   431  		}
   432  	}
   433  
   434  	// For everything we return result.
   435  	return decodeArg(dataConverter, result, to)
   436  }
   437  
   438  func setActivityParametersIfNotExist(ctx Context) Context {
   439  	params := getActivityOptions(ctx)
   440  	var newParams activityOptions
   441  	if params != nil {
   442  		newParams = *params
   443  		if params.RetryPolicy != nil {
   444  			var newRetryPolicy shared.RetryPolicy
   445  			newRetryPolicy = *newParams.RetryPolicy
   446  			newParams.RetryPolicy = &newRetryPolicy
   447  		}
   448  	}
   449  	return WithValue(ctx, activityOptionsContextKey, &newParams)
   450  }
   451  
   452  func setLocalActivityParametersIfNotExist(ctx Context) Context {
   453  	params := getLocalActivityOptions(ctx)
   454  	var newParams localActivityOptions
   455  	if params != nil {
   456  		newParams = *params
   457  	}
   458  	return WithValue(ctx, localActivityOptionsContextKey, &newParams)
   459  }