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 }