go.uber.org/cadence@v1.2.9/internal/workflow.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 import ( 25 "errors" 26 "fmt" 27 "reflect" 28 "strings" 29 "time" 30 31 "github.com/uber-go/tally" 32 "go.uber.org/zap" 33 34 s "go.uber.org/cadence/.gen/go/shared" 35 "go.uber.org/cadence/internal/common" 36 "go.uber.org/cadence/internal/common/backoff" 37 ) 38 39 var ( 40 errDomainNotSet = errors.New("domain is not set") 41 errTaskListNotSet = errors.New("task list is not set") 42 errWorkflowIDNotSet = errors.New("workflowId is not set") 43 errLocalActivityParamsBadRequest = errors.New("missing local activity parameters through context, check LocalActivityOptions") 44 errActivityParamsBadRequest = errors.New("missing activity parameters through context, check ActivityOptions") 45 errWorkflowOptionBadRequest = errors.New("missing workflow options through context, check WorkflowOptions") 46 errSearchAttributesNotSet = errors.New("search attributes is empty") 47 ) 48 49 type ( 50 // Channel must be used in workflows instead of a native Go chan. 51 // 52 // Use workflow.NewChannel(ctx) to create an unbuffered Channel instance, 53 // workflow.NewBufferedChannel(ctx, size) to create a Channel which has a buffer, 54 // or workflow.GetSignalChannel(ctx, "name") to get a Channel that contains data sent to this workflow by a call to 55 // SignalWorkflow (e.g. on the Client, or similar methods like SignalExternalWorkflow or SignalChildWorkflow). 56 // 57 // Both NewChannel and NewBufferedChannel have "Named" constructors as well. 58 // These names will be visible in stack-trace queries, so they can help with debugging, but they do not otherwise 59 // impact behavior at all, and are not recorded anywhere (so you can change them without versioning your code). 60 // 61 // Also note that channels created by NewChannel and NewBufferedChannel do not do any serialization or 62 // deserialization - you will receive whatever value was sent, and non-(de)serializable values like function 63 // references and interfaces are fine, the same as using a normal Go channel. 64 // 65 // Signal channels, however, contain whatever bytes were sent to your workflow, and the values must be decoded into 66 // the output value. By default, this means that Receive(ctx, &out) will use json.Unmarshal(data, &out), but this 67 // can be overridden at a worker level (worker.Options) or at a context level (workflow.WithDataConverter(ctx, dc)). 68 // 69 // You are able to send values to your own signal channels, and these values will behave the same as they do in 70 // normal channels (i.e. they will not be (de)serialized). However, doing so is not generally recommended, as 71 // mixing the value types can increase the risk that you fail to read a value, causing values to be lost. See 72 // Receive for more details about that behavior. 73 Channel interface { 74 // Receive blocks until it receives a value, and then assigns the received value to the provided pointer. 75 // It returns false when the Channel is closed and all data has already been consumed from the Channel, in the 76 // same way as Go channel reads work, but the assignment only occurs if there was a value in the Channel. 77 // 78 // This is technically equivalent to: 79 // received, ok := <- aChannel: 80 // if ok { 81 // *valuePtr = received 82 // } 83 // 84 // But if your output values are zero values, this is equivalent to a normal channel read: 85 // value, ok <- aChannel 86 // 87 // valuePtr must be assignable, and will be used to assign (for in-memory data in regular channels) or decode 88 // (for signal channels) the data in the channel. 89 // 90 // If decoding or assigning fails: 91 // - an error will be logged 92 // - the value will be dropped from the channel 93 // - Receive will automatically try again 94 // - This will continue until a successful value is found, or the channel is emptied and it resumes blocking. 95 // Closed channels with no values will always succeed, but they will not change valuePtr. 96 // 97 // Go would normally prevent incorrect-type failures like this at compile time, but the same cannot be done 98 // here. If you need to "try" to assign to multiple things, similar to a Future you can use: 99 // - for signal channels, a []byte pointer. This will give you the raw data that Cadence received, and no 100 // decoding will be attempted, so you can try it yourself. 101 // - for other channels, an interface{} pointer. All values are interfaces, so this will never fail, and you 102 // can inspect the type with reflection or type assertions. 103 Receive(ctx Context, valuePtr interface{}) (ok bool) 104 105 // ReceiveAsync tries to Receive from Channel without blocking. 106 // If there is data available from the Channel, it assigns the data to valuePtr and returns true. 107 // Otherwise, it returns false immediately. 108 // 109 // This is technically equivalent to: 110 // select { 111 // case received, ok := <- aChannel: 112 // if ok { 113 // *valuePtr = received 114 // } 115 // default: 116 // // no value was read 117 // ok = false 118 // } 119 // 120 // But if your output values are zero values, this is equivalent to a simpler form: 121 // select { 122 // case value, ok := <- aChannel: 123 // default: 124 // // no value was read 125 // ok = false 126 // } 127 // 128 // Decoding or assigning failures are handled like Receive. 129 ReceiveAsync(valuePtr interface{}) (ok bool) 130 131 // ReceiveAsyncWithMoreFlag is the same as ReceiveAsync, with an extra return to indicate if there could be 132 // more values from the Channel in the future. 133 // `more` is false only when Channel is closed and the read failed (empty). 134 // 135 // This is technically equivalent to: 136 // select { 137 // case received, ok := <- aChannel: 138 // if ok { 139 // *valuePtr = received 140 // } 141 // more = ok 142 // default: 143 // // no value was read 144 // ok = false 145 // // but the read would have blocked, so the channel is not closed 146 // more = true 147 // } 148 // 149 // But if your output values are zero values, this is equivalent to a simpler form: 150 // select { 151 // case value, ok := <- aChannel: 152 // more = ok 153 // default: 154 // // no value was read 155 // ok = false 156 // // but the read would have blocked, so the channel is not closed 157 // more = true 158 // } 159 // 160 // Decoding or assigning failures are handled like Receive. 161 ReceiveAsyncWithMoreFlag(valuePtr interface{}) (ok bool, more bool) 162 163 // Send blocks until the data is sent. 164 // 165 // This is equivalent to `aChannel <- v`. 166 Send(ctx Context, v interface{}) 167 168 // SendAsync will try to send without blocking. 169 // It returns true if the data was sent (i.e. there was room in the buffer, or a reader was waiting to receive 170 // it), otherwise it returns false. 171 // 172 // This is equivalent to: 173 // select { 174 // case aChannel <- v: ok = true 175 // default: ok = false 176 // } 177 SendAsync(v interface{}) (ok bool) 178 179 // Close closes the Channel, and prohibits subsequent sends. 180 // As with a normal Go channel that has been closed, sending to a closed channel will panic. 181 Close() 182 } 183 184 // Selector must be used in workflows instead of a native Go select statement. 185 // 186 // Use workflow.NewSelector(ctx) to create a Selector instance, and then add cases to it with its methods. 187 // The interface is intended to simulate Go's select statement, and any Go select can be fairly trivially rewritten 188 // for a Selector with effectively identical behavior. 189 // 190 // For example, normal Go code like below (which will receive values forever, until idle for an hour): 191 // chA := make(chan int) 192 // chB := make(chan int) 193 // counter := 0 194 // for { 195 // select { 196 // case x := <- chA: 197 // counter += i 198 // case y := <- chB: 199 // counter += i 200 // case <- time.After(time.Hour): 201 // break 202 // } 203 // } 204 // can be written as: 205 // chA := workflow.NewChannel(ctx) 206 // chB := workflow.NewChannel(ctx) 207 // counter := 0 208 // for { 209 // timedout := false 210 // s := workflow.NewSelector(ctx) 211 // s.AddReceive(chA, func(c workflow.Channel, more bool) { 212 // var x int 213 // c.Receive(ctx, &x) 214 // counter += i 215 // }) 216 // s.AddReceive(chB, func(c workflow.Channel, more bool) { 217 // var y int 218 // c.Receive(ctx, &y) 219 // counter += i 220 // }) 221 // s.AddFuture(workflow.NewTimer(ctx, time.Hour), func(f workflow.Future) { 222 // timedout = true 223 // }) 224 // s.Select(ctx) 225 // if timedout { 226 // break 227 // } 228 // } 229 // 230 // You can create a new Selector as needed or mutate one and call Select multiple times, but note that: 231 // 232 // 1. AddFuture will not behave the same across both patterns. Read AddFuture for more details. 233 // 234 // 2. There is no way to remove a case from a Selector, so you must make a new Selector to "remove" them. 235 // 236 // Finally, note that Select will not return until a condition's needs are met, like a Go selector - canceling the 237 // Context used to construct the Selector, or the Context used to Select, will not (directly) unblock a Select call. 238 // Read Select for more details. 239 Selector interface { 240 // AddReceive waits until a value can be received from a channel. 241 // f is invoked when the channel has data or is closed. 242 // 243 // This is equivalent to `case v, ok := <- aChannel`, and `ok` will only be false when 244 // the channel is both closed and no data was received. 245 // 246 // When f is invoked, the data (or closed state) remains untouched in the channel, so 247 // you need to `c.Receive(ctx, &out)` (or `c.ReceiveAsync(&out)`) to remove and decode the value. 248 // Failure to do this is not an error - the value will simply remain in the channel until a future 249 // Receive retrieves it. 250 // 251 // The `ok` argument will match what a call to c.Receive would return (on a successful read), so it 252 // may be used to check for closed + empty channels without needing to try to read from the channel. 253 // See Channel.Receive for additional details about reading from channels. 254 AddReceive(c Channel, f func(c Channel, ok bool)) Selector 255 // AddSend waits to send a value to a channel. 256 // f is invoked when the value was successfully sent to the channel. 257 // 258 // This is equivalent to `case aChannel <- value`. 259 // 260 // Unlike AddReceive, the value has already been sent on the channel when f is invoked. 261 AddSend(c Channel, v interface{}, f func()) Selector 262 // AddFuture waits until a Future is ready, and then invokes f only once. 263 // If the Future is ready before Select is called, it is eligible to be invoked immediately. 264 // 265 // There is no direct equivalent in a native Go select statement. 266 // It was added because Futures are common in Cadence code, and some patterns are much simpler with it. 267 // 268 // Each call to AddFuture will invoke its f at most one time, regardless of how many times Select is called. 269 // This means, for a Future that is (or will be) ready: 270 // - Adding the Future once, then calling Select twice, will invoke the callback once with the first Select 271 // call, and then wait for other Selector conditions in the second Select call (or block forever if there are 272 // no other eligible conditions). 273 // - Adding the same Future twice, then calling Select twice, will invoke each callback once. 274 // - Adding the same Future to two different Selectors, then calling Select once on each Selector, will invoke 275 // each Selector's callback once. 276 // 277 // Therefore, with a Future "f" that is or will become ready, this is an infinite loop that will consume as much 278 // CPU as possible: 279 // for { 280 // workflow.NewSelector(ctx).AddFuture(f, func(f workflow.Future){}).Select(ctx) 281 // } 282 // While this will loop once, and then wait idle forever: 283 // s := workflow.NewSelector(ctx).AddFuture(f, func(f workflow.Future){}) 284 // for { 285 // s.Select(ctx) 286 // } 287 AddFuture(future Future, f func(f Future)) Selector 288 // AddDefault adds a default branch to the selector. 289 // f is invoked immediately when none of the other conditions (AddReceive, AddSend, AddFuture) are met for a 290 // Select call. 291 // 292 // This is equivalent to a `default:` case. 293 // 294 // Note that this applies to each Select call. If you create a Selector with only one AddDefault, and then call 295 // Select on it twice, f will be invoked twice. 296 AddDefault(f func()) 297 // Select waits for one of the added conditions to be met and invokes the callback as described above. 298 // If no condition is met, Select will block until one or more are available, then one callback will be invoked. 299 // If no condition is ever met, Select will block forever. 300 // 301 // Note that Select does not return an error, and does not stop waiting if its Context is canceled. 302 // This mimics a native Go select statement, which has no way to be interrupted except for its listed cases. 303 // 304 // If you wish to stop Selecting when the Context is canceled, use AddReceive with the Context's Done() channel, 305 // in the same way as you would use a `case <- ctx.Done():` in a Go select statement. E.g.: 306 // cancelled := false 307 // s := workflow.NewSelector(ctx) 308 // s.AddFuture(f, func(f workflow.Future) {}) // assume this is never ready 309 // s.AddReceive(ctx.Done(), func(c workflow.Channel, more bool) { 310 // // this will be invoked when the Context is cancelled for any reason, 311 // // and more will be false. 312 // cancelled = true 313 // }) 314 // s.Select(ctx) 315 // if cancelled { 316 // // this will be executed 317 // } 318 Select(ctx Context) 319 } 320 321 // WaitGroup must be used instead of native go sync.WaitGroup by 322 // workflow code. Use workflow.NewWaitGroup(ctx) method to create 323 // a new WaitGroup instance 324 WaitGroup interface { 325 Add(delta int) 326 Done() 327 Wait(ctx Context) 328 } 329 330 // Future represents the result of an asynchronous computation. 331 Future interface { 332 // Get blocks until the future is ready. 333 // When ready it either returns the Future's contained error, or assigns the contained value to the output var. 334 // Failures to assign or decode the value will panic. 335 // 336 // Two common patterns to retrieve data are: 337 // var out string 338 // // this will assign the string value, which may be "", or an error and leave out as "". 339 // err := f.Get(ctx, &out) 340 // and 341 // var out *string 342 // // this will assign the string value, which may be "" or nil, or an error and leave out as nil. 343 // err := f.Get(ctx, &out) 344 // 345 // The valuePtr parameter can be nil when the encoded result value is not needed: 346 // err := f.Get(ctx, nil) 347 // 348 // Futures with values set in-memory via a call to their Settable's methods can be retrieved without knowing the 349 // type with an interface, i.e. this will not ever panic: 350 // var out interface{} 351 // // this will assign the same value that was set, 352 // // and you can check its type with reflection or type assertions. 353 // err := f.Get(ctx, &out) 354 // 355 // Futures with encoded data from e.g. activities or child workflows can bypass decoding with a byte slice, and 356 // similarly this will not ever panic: 357 // var out []byte 358 // // out will contain the raw bytes given to Cadence's servers, you should decode it however is necessary 359 // err := f.Get(ctx, &out) // err can only be the Future's contained error 360 Get(ctx Context, valuePtr interface{}) error 361 362 // IsReady will return true Get is guaranteed to not block. 363 IsReady() bool 364 } 365 366 // Settable is used to set value or error on a future. 367 // See more: workflow.NewFuture(ctx). 368 Settable interface { 369 Set(value interface{}, err error) 370 SetValue(value interface{}) 371 SetError(err error) 372 Chain(future Future) // Value (or error) of the future become the same of the chained one. 373 } 374 375 // ChildWorkflowFuture represents the result of a child workflow execution 376 ChildWorkflowFuture interface { 377 Future 378 // GetChildWorkflowExecution returns a future that will be ready when child workflow execution started. You can 379 // get the WorkflowExecution of the child workflow from the future. Then you can use Workflow ID and RunID of 380 // child workflow to cancel or send signal to child workflow. 381 // childWorkflowFuture := workflow.ExecuteChildWorkflow(ctx, child, ...) 382 // var childWE WorkflowExecution 383 // if err := childWorkflowFuture.GetChildWorkflowExecution().Get(ctx, &childWE); err == nil { 384 // // child workflow started, you can use childWE to get the WorkflowID and RunID of child workflow 385 // } 386 GetChildWorkflowExecution() Future 387 388 // SignalWorkflowByID sends a signal to the child workflow. This call will block until child workflow is started. 389 SignalChildWorkflow(ctx Context, signalName string, data interface{}) Future 390 } 391 392 // WorkflowType identifies a workflow type. 393 WorkflowType struct { 394 Name string 395 } 396 397 // WorkflowExecution Details. 398 WorkflowExecution struct { 399 ID string 400 RunID string 401 } 402 403 // EncodedValue is type alias used to encapsulate/extract encoded result from workflow/activity. 404 EncodedValue struct { 405 value []byte 406 dataConverter DataConverter 407 } 408 // Version represents a change version. See GetVersion call. 409 Version int 410 411 // ChildWorkflowOptions stores all child workflow specific parameters that will be stored inside of a Context. 412 // The current timeout resolution implementation is in seconds and uses math.Ceil(d.Seconds()) as the duration. But is 413 // subjected to change in the future. 414 ChildWorkflowOptions struct { 415 // Domain of the child workflow. 416 // Optional: the current workflow (parent)'s domain will be used if this is not provided. 417 Domain string 418 419 // WorkflowID of the child workflow to be scheduled. 420 // Optional: an auto generated workflowID will be used if this is not provided. 421 WorkflowID string 422 423 // TaskList that the child workflow needs to be scheduled on. 424 // Optional: the parent workflow task list will be used if this is not provided. 425 TaskList string 426 427 // ExecutionStartToCloseTimeout - The end to end timeout for the child workflow execution. 428 // Mandatory: no default 429 ExecutionStartToCloseTimeout time.Duration 430 431 // TaskStartToCloseTimeout - The decision task timeout for the child workflow. 432 // Optional: default is 10s if this is not provided (or if 0 is provided). 433 TaskStartToCloseTimeout time.Duration 434 435 // WaitForCancellation - Whether to wait for cancelled child workflow to be ended (child workflow can be ended 436 // as: completed/failed/timedout/terminated/canceled) 437 // Optional: default false 438 WaitForCancellation bool 439 440 // WorkflowIDReusePolicy - Whether server allow reuse of workflow ID, can be useful 441 // for dedup logic if set to WorkflowIdReusePolicyRejectDuplicate 442 WorkflowIDReusePolicy WorkflowIDReusePolicy 443 444 // RetryPolicy specify how to retry child workflow if error happens. 445 // Optional: default is no retry 446 RetryPolicy *RetryPolicy 447 448 // CronSchedule - Optional cron schedule for workflow. If a cron schedule is specified, the workflow will run 449 // as a cron based on the schedule. The scheduling will be based on UTC time. Schedule for next run only happen 450 // after the current run is completed/failed/timeout. If a RetryPolicy is also supplied, and the workflow failed 451 // or timeout, the workflow will be retried based on the retry policy. While the workflow is retrying, it won't 452 // schedule its next run. If next schedule is due while workflow is running (or retrying), then it will skip that 453 // schedule. Cron workflow will not stop until it is terminated or cancelled (by returning cadence.CanceledError). 454 // The cron spec is as following: 455 // ┌───────────── minute (0 - 59) 456 // │ ┌───────────── hour (0 - 23) 457 // │ │ ┌───────────── day of the month (1 - 31) 458 // │ │ │ ┌───────────── month (1 - 12) 459 // │ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday) 460 // │ │ │ │ │ 461 // │ │ │ │ │ 462 // * * * * * 463 CronSchedule string 464 465 // Memo - Optional non-indexed info that will be shown in list workflow. 466 Memo map[string]interface{} 467 468 // SearchAttributes - Optional indexed info that can be used in query of List/Scan/Count workflow APIs (only 469 // supported when Cadence server is using ElasticSearch). The key and value type must be registered on Cadence server side. 470 // Use GetSearchAttributes API to get valid key and corresponding value type. 471 SearchAttributes map[string]interface{} 472 473 // ParentClosePolicy - Optional policy to decide what to do for the child. 474 // Default is Terminate (if onboarded to this feature) 475 ParentClosePolicy ParentClosePolicy 476 477 // Bugports allows opt-in enabling of older, possibly buggy behavior, primarily intended to allow temporarily 478 // emulating old behavior until a fix is deployed. 479 // 480 // Bugports are always deprecated and may be removed in future versions. 481 // Generally speaking they will *likely* remain in place for one minor version, and then they may be removed to 482 // allow cleaning up the additional code complexity that they cause. 483 // 484 // Deprecated: All bugports are always deprecated and may be removed at any time. 485 Bugports Bugports 486 } 487 488 // Bugports allows opt-in enabling of older, possibly buggy behavior, primarily intended to allow temporarily 489 // emulating old behavior until a fix is deployed. 490 // By default, bugs (especially rarely-occurring ones) are fixed and all users are opted into the new behavior. 491 // Back-ported buggy behavior *may* be available via these flags. 492 // 493 // Fields in here are NOT guaranteed to be stable. They will almost certainly be removed in the next major 494 // release, and might be removed earlier if a need arises, e.g. if the historical behavior causes too much of an 495 // increase in code complexity. 496 // 497 // See each individual field for details. 498 // 499 // Bugports are always deprecated and may be removed in future versions. 500 // Generally speaking they will *likely* remain in place for one minor version, and then they may be removed to 501 // allow cleaning up the additional code complexity that they cause. 502 // 503 // DEPRECATED: All bugports are always deprecated and may be removed at any time. 504 Bugports struct { 505 // StartChildWorkflowsOnCanceledContext allows emulating older, buggy behavior that existed prior to v0.18.4. 506 // 507 // Prior to the fix, child workflows would be started and keep running when their context was canceled in two 508 // situations: 509 // 1) when the context was canceled before ExecuteChildWorkflow is called, and 510 // 2) when the context was canceled after ExecuteChildWorkflow but before the child workflow was started. 511 // 512 // 1 is unfortunately easy to trigger, though many workflows will encounter an error earlier and not reach the 513 // child-workflow-executing code. 2 is expected to be very rare in practice. 514 // 515 // To permanently emulate old behavior, use a disconnected context when starting child workflows, and 516 // cancel it only after `childfuture.GetWorkflowExecution().Get(...)` returns. This can be used when this flag 517 // is removed in the future. 518 // 519 // If you have currently-broken workflows and need to repair them, there are two primary options: 520 // 521 // 1: Check the BinaryChecksum value of your new deploy and/or of the decision that is currently failing 522 // workflows. Then set this flag when replaying history on those not-fixed checksums. Concretely, this means 523 // checking both `workflow.GetInfo(ctx).BinaryChecksum` (note that sufficiently old clients may not have 524 // recorded a value, and it may be nil) and `workflow.IsReplaying(ctx)`. 525 // 526 // 2: Reset broken workflows back to either before the buggy behavior was recorded, or before the fixed behavior 527 // was deployed. A "bad binary" reset type can do the latter in bulk, see the CLI's 528 // `cadence workflow reset-batch --reset_type BadBinary --help` for details. For the former, check the failing 529 // histories, identify the point at which the bug occurred, and reset to prior to that decision task. 530 // 531 // Added in 0.18.4, this may be removed in or after v0.19.0, so please migrate off of it ASAP. 532 // 533 // Deprecated: All bugports are always deprecated and may be removed at any time. 534 StartChildWorkflowsOnCanceledContext bool 535 } 536 ) 537 538 // RegisterWorkflowOptions consists of options for registering a workflow 539 type RegisterWorkflowOptions struct { 540 Name string 541 // Workflow type name is equal to function name instead of fully qualified name including function package. 542 // This option has no effect when explicit Name is provided. 543 EnableShortName bool 544 DisableAlreadyRegisteredCheck bool 545 } 546 547 // RegisterWorkflow - registers a workflow function with the framework. 548 // The public form is: workflow.Register(...) 549 // A workflow takes a cadence context and input and returns a (result, error) or just error. 550 // Examples: 551 // 552 // func sampleWorkflow(ctx workflow.Context, input []byte) (result []byte, err error) 553 // func sampleWorkflow(ctx workflow.Context, arg1 int, arg2 string) (result []byte, err error) 554 // func sampleWorkflow(ctx workflow.Context) (result []byte, err error) 555 // func sampleWorkflow(ctx workflow.Context, arg1 int) (result string, err error) 556 // 557 // Serialization of all primitive types, structures is supported ... except channels, functions, variadic, unsafe pointer. 558 // This method calls panic if workflowFunc doesn't comply with the expected format. 559 // Deprecated: Global workflow registration methods are replaced by equivalent Worker instance methods. 560 // This method is kept to maintain backward compatibility and should not be used. 561 func RegisterWorkflow(workflowFunc interface{}) { 562 RegisterWorkflowWithOptions(workflowFunc, RegisterWorkflowOptions{}) 563 } 564 565 // RegisterWorkflowWithOptions registers the workflow function with options. 566 // The public form is: workflow.RegisterWithOptions(...) 567 // The user can use options to provide an external name for the workflow or leave it empty if no 568 // external name is required. This can be used as 569 // 570 // workflow.RegisterWithOptions(sampleWorkflow, RegisterWorkflowOptions{}) 571 // workflow.RegisterWithOptions(sampleWorkflow, RegisterWorkflowOptions{Name: "foo"}) 572 // 573 // A workflow takes a cadence context and input and returns a (result, error) or just error. 574 // Examples: 575 // 576 // func sampleWorkflow(ctx workflow.Context, input []byte) (result []byte, err error) 577 // func sampleWorkflow(ctx workflow.Context, arg1 int, arg2 string) (result []byte, err error) 578 // func sampleWorkflow(ctx workflow.Context) (result []byte, err error) 579 // func sampleWorkflow(ctx workflow.Context, arg1 int) (result string, err error) 580 // 581 // Serialization of all primitive types, structures is supported ... except channels, functions, variadic, unsafe pointer. 582 // This method calls panic if workflowFunc doesn't comply with the expected format or tries to register the same workflow 583 // type name twice. Use workflow.RegisterOptions.DisableAlreadyRegisteredCheck to allow multiple registrations. 584 // Deprecated: Global workflow registration methods are replaced by equivalent Worker instance methods. 585 // This method is kept to maintain backward compatibility and should not be used. 586 func RegisterWorkflowWithOptions(workflowFunc interface{}, opts RegisterWorkflowOptions) { 587 registry := getGlobalRegistry() 588 registry.RegisterWorkflowWithOptions(workflowFunc, opts) 589 } 590 591 // GetRegisteredWorkflowTypes returns the registered workflow function/alias names. 592 // The public form is: workflow.GetRegisteredWorkflowTypes(...) 593 func GetRegisteredWorkflowTypes() []string { 594 registry := getGlobalRegistry() 595 return registry.GetRegisteredWorkflowTypes() 596 } 597 598 // Await blocks the calling thread until condition() returns true 599 // Returns CanceledError if the ctx is canceled. 600 func Await(ctx Context, condition func() bool) error { 601 state := getState(ctx) 602 defer state.unblocked() 603 604 for !condition() { 605 doneCh := ctx.Done() 606 // TODO: Consider always returning a channel 607 if doneCh != nil { 608 if _, more := doneCh.ReceiveAsyncWithMoreFlag(nil); !more { 609 return NewCanceledError("Await context cancelled") 610 } 611 } 612 state.yield("Await") 613 } 614 return nil 615 } 616 617 // NewChannel create new Channel instance 618 func NewChannel(ctx Context) Channel { 619 state := getState(ctx) 620 state.dispatcher.channelSequence++ 621 return NewNamedChannel(ctx, fmt.Sprintf("chan-%v", state.dispatcher.channelSequence)) 622 } 623 624 // NewNamedChannel create new Channel instance with a given human readable name. 625 // Name appears in stack traces that are blocked on this channel. 626 func NewNamedChannel(ctx Context, name string) Channel { 627 env := getWorkflowEnvironment(ctx) 628 return &channelImpl{name: name, dataConverter: getDataConverterFromWorkflowContext(ctx), env: env} 629 } 630 631 // NewBufferedChannel create new buffered Channel instance 632 func NewBufferedChannel(ctx Context, size int) Channel { 633 env := getWorkflowEnvironment(ctx) 634 return &channelImpl{size: size, dataConverter: getDataConverterFromWorkflowContext(ctx), env: env} 635 } 636 637 // NewNamedBufferedChannel create new BufferedChannel instance with a given human readable name. 638 // Name appears in stack traces that are blocked on this Channel. 639 func NewNamedBufferedChannel(ctx Context, name string, size int) Channel { 640 env := getWorkflowEnvironment(ctx) 641 return &channelImpl{name: name, size: size, dataConverter: getDataConverterFromWorkflowContext(ctx), env: env} 642 } 643 644 // NewSelector creates a new Selector instance. 645 func NewSelector(ctx Context) Selector { 646 state := getState(ctx) 647 state.dispatcher.selectorSequence++ 648 return NewNamedSelector(ctx, fmt.Sprintf("selector-%v", state.dispatcher.selectorSequence)) 649 } 650 651 // NewNamedSelector creates a new Selector instance with a given human readable name. 652 // Name appears in stack traces that are blocked on this Selector. 653 func NewNamedSelector(ctx Context, name string) Selector { 654 return &selectorImpl{name: name} 655 } 656 657 // NewWaitGroup creates a new WaitGroup instance. 658 func NewWaitGroup(ctx Context) WaitGroup { 659 f, s := NewFuture(ctx) 660 return &waitGroupImpl{future: f, settable: s} 661 } 662 663 // Go creates a new coroutine. It has similar semantic to goroutine in a context of the workflow. 664 func Go(ctx Context, f func(ctx Context)) { 665 state := getState(ctx) 666 state.dispatcher.newCoroutine(ctx, f) 667 } 668 669 // GoNamed creates a new coroutine with a given human readable name. 670 // It has similar semantic to goroutine in a context of the workflow. 671 // Name appears in stack traces that are blocked on this Channel. 672 func GoNamed(ctx Context, name string, f func(ctx Context)) { 673 state := getState(ctx) 674 state.dispatcher.newNamedCoroutine(ctx, name, f) 675 } 676 677 // NewFuture creates a new future as well as associated Settable that is used to set its value. 678 func NewFuture(ctx Context) (Future, Settable) { 679 impl := &futureImpl{channel: NewChannel(ctx).(*channelImpl)} 680 return impl, impl 681 } 682 683 func (wc *workflowEnvironmentInterceptor) ExecuteWorkflow(ctx Context, workflowType string, inputArgs ...interface{}) (results []interface{}) { 684 args := []reflect.Value{reflect.ValueOf(ctx)} 685 for _, arg := range inputArgs { 686 // []byte arguments are not serialized 687 switch arg.(type) { 688 case []byte: 689 args = append(args, reflect.ValueOf(arg)) 690 default: 691 args = append(args, reflect.ValueOf(arg).Elem()) 692 } 693 } 694 fnValue := reflect.ValueOf(wc.fn) 695 retValues := fnValue.Call(args) 696 for _, r := range retValues { 697 results = append(results, r.Interface()) 698 } 699 return 700 } 701 702 // ExecuteActivity requests activity execution in the context of a workflow. 703 // Context can be used to pass the settings for this activity. 704 // For example: task list that this need to be routed, timeouts that need to be configured. 705 // Use ActivityOptions to pass down the options. 706 // 707 // ao := ActivityOptions{ 708 // TaskList: "exampleTaskList", 709 // ScheduleToStartTimeout: 10 * time.Second, 710 // StartToCloseTimeout: 5 * time.Second, 711 // ScheduleToCloseTimeout: 10 * time.Second, 712 // HeartbeatTimeout: 0, 713 // } 714 // ctx := WithActivityOptions(ctx, ao) 715 // 716 // Or to override a single option 717 // 718 // ctx := WithTaskList(ctx, "exampleTaskList") 719 // 720 // Input activity is either an activity name (string) or a function representing an activity that is getting scheduled. 721 // Input args are the arguments that need to be passed to the scheduled activity. 722 // 723 // If the activity failed to complete then the future get error would indicate the failure, and it can be one of 724 // CustomError, TimeoutError, CanceledError, PanicError, GenericError. 725 // You can cancel the pending activity using context(workflow.WithCancel(ctx)) and that will fail the activity with 726 // error CanceledError. 727 // 728 // ExecuteActivity returns Future with activity result or failure. 729 func ExecuteActivity(ctx Context, activity interface{}, args ...interface{}) Future { 730 i := getWorkflowInterceptor(ctx) 731 registry := getRegistryFromWorkflowContext(ctx) 732 activityType := getActivityFunctionName(registry, activity) 733 return i.ExecuteActivity(ctx, activityType, args...) 734 } 735 736 func (wc *workflowEnvironmentInterceptor) ExecuteActivity(ctx Context, typeName string, args ...interface{}) Future { 737 // Validate type and its arguments. 738 dataConverter := getDataConverterFromWorkflowContext(ctx) 739 registry := getRegistryFromWorkflowContext(ctx) 740 future, settable := newDecodeFuture(ctx, typeName) 741 activityType, err := getValidatedActivityFunction(typeName, args, registry) 742 if err != nil { 743 settable.Set(nil, err) 744 return future 745 } 746 // Validate context options. 747 options, err := getValidatedActivityOptions(ctx) 748 if err != nil { 749 settable.Set(nil, err) 750 return future 751 } 752 753 // Validate session state. 754 if sessionInfo := getSessionInfo(ctx); sessionInfo != nil { 755 isCreationActivity := isSessionCreationActivity(typeName) 756 if sessionInfo.sessionState == sessionStateFailed && !isCreationActivity { 757 settable.Set(nil, ErrSessionFailed) 758 return future 759 } 760 if sessionInfo.sessionState == sessionStateOpen && !isCreationActivity { 761 // Use session tasklist 762 oldTaskListName := options.TaskListName 763 options.TaskListName = sessionInfo.tasklist 764 defer func() { 765 options.TaskListName = oldTaskListName 766 }() 767 } 768 } 769 770 // Retrieve headers from context to pass them on 771 header := getHeadersFromContext(ctx) 772 773 input, err := encodeArgs(dataConverter, args) 774 if err != nil { 775 panic(err) 776 } 777 778 params := executeActivityParams{ 779 activityOptions: *options, 780 ActivityType: *activityType, 781 Input: input, 782 DataConverter: dataConverter, 783 Header: header, 784 } 785 786 ctxDone, cancellable := ctx.Done().(*channelImpl) 787 cancellationCallback := &receiveCallback{} 788 a := getWorkflowEnvironment(ctx).ExecuteActivity(params, func(r []byte, e error) { 789 settable.Set(r, e) 790 if cancellable { 791 // future is done, we don't need the cancellation callback anymore. 792 ctxDone.removeReceiveCallback(cancellationCallback) 793 } 794 }) 795 796 if cancellable { 797 cancellationCallback.fn = func(v interface{}, more bool) bool { 798 if ctx.Err() == ErrCanceled { 799 wc.env.RequestCancelActivity(a.activityID) 800 } 801 return false 802 } 803 _, ok, more := ctxDone.receiveAsyncImpl(cancellationCallback) 804 if ok || !more { 805 cancellationCallback.fn(nil, more) 806 } 807 } 808 return future 809 } 810 811 // ExecuteLocalActivity requests to run a local activity. A local activity is like a regular activity with some key 812 // differences: 813 // * Local activity is scheduled and run by the workflow worker locally. 814 // * Local activity does not need Cadence server to schedule activity task and does not rely on activity worker. 815 // * No need to register local activity. 816 // * The parameter activity to ExecuteLocalActivity() must be a function. 817 // * Local activity is for short living activities (usually finishes within seconds). 818 // * Local activity cannot heartbeat. 819 // 820 // Context can be used to pass the settings for this local activity. 821 // For now there is only one setting for timeout to be set: 822 // 823 // lao := LocalActivityOptions{ 824 // ScheduleToCloseTimeout: 5 * time.Second, 825 // } 826 // ctx := WithLocalActivityOptions(ctx, lao) 827 // 828 // The timeout here should be relative shorter than the DecisionTaskStartToCloseTimeout of the workflow. If you need a 829 // longer timeout, you probably should not use local activity and instead should use regular activity. Local activity is 830 // designed to be used for short living activities (usually finishes within seconds). 831 // 832 // Input args are the arguments that will to be passed to the local activity. The input args will be hand over directly 833 // to local activity function without serialization/deserialization because we don't need to pass the input across process 834 // boundary. However, the result will still go through serialization/deserialization because we need to record the result 835 // as history to cadence server so if the workflow crashes, a different worker can replay the history without running 836 // the local activity again. 837 // 838 // If the activity failed to complete then the future get error would indicate the failure, and it can be one of 839 // CustomError, TimeoutError, CanceledError, PanicError, GenericError. 840 // You can cancel the pending activity by cancel the context(workflow.WithCancel(ctx)) and that will fail the activity 841 // with error CanceledError. 842 // 843 // ExecuteLocalActivity returns Future with local activity result or failure. 844 func ExecuteLocalActivity(ctx Context, activity interface{}, args ...interface{}) Future { 845 i := getWorkflowInterceptor(ctx) 846 env := getWorkflowEnvironment(ctx) 847 activityType := getActivityFunctionName(env.GetRegistry(), activity) 848 ctx = WithValue(ctx, localActivityFnContextKey, activity) 849 return i.ExecuteLocalActivity(ctx, activityType, args...) 850 } 851 852 func (wc *workflowEnvironmentInterceptor) ExecuteLocalActivity(ctx Context, activityType string, args ...interface{}) Future { 853 header := getHeadersFromContext(ctx) 854 activityFn := ctx.Value(localActivityFnContextKey) 855 if activityFn == nil { 856 panic("ExecuteLocalActivity: Expected context key " + localActivityFnContextKey + " is missing") 857 } 858 859 future, settable := newDecodeFuture(ctx, activityFn) 860 if err := validateFunctionArgs(activityFn, args, false); err != nil { 861 settable.Set(nil, err) 862 return future 863 } 864 options, err := getValidatedLocalActivityOptions(ctx) 865 if err != nil { 866 settable.Set(nil, err) 867 return future 868 } 869 params := &executeLocalActivityParams{ 870 localActivityOptions: *options, 871 ActivityFn: activityFn, 872 ActivityType: activityType, 873 InputArgs: args, 874 WorkflowInfo: GetWorkflowInfo(ctx), 875 DataConverter: getDataConverterFromWorkflowContext(ctx), 876 ScheduledTime: Now(ctx), // initial scheduled time 877 Header: header, 878 } 879 880 Go(ctx, func(ctx Context) { 881 for { 882 f := wc.scheduleLocalActivity(ctx, params) 883 var result []byte 884 err := f.Get(ctx, &result) 885 if retryErr, ok := err.(*needRetryError); ok && retryErr.Backoff > 0 { 886 // Backoff for retry 887 Sleep(ctx, retryErr.Backoff) 888 // increase the attempt, and retry the local activity 889 params.Attempt = retryErr.Attempt + 1 890 continue 891 } 892 893 // not more retry, return whatever is received. 894 settable.Set(result, err) 895 return 896 } 897 }) 898 899 return future 900 } 901 902 type needRetryError struct { 903 Backoff time.Duration 904 Attempt int32 905 } 906 907 func (e *needRetryError) Error() string { 908 return fmt.Sprintf("Retry backoff: %v, Attempt: %v", e.Backoff, e.Attempt) 909 } 910 911 func (wc *workflowEnvironmentInterceptor) scheduleLocalActivity(ctx Context, params *executeLocalActivityParams) Future { 912 f := &futureImpl{channel: NewChannel(ctx).(*channelImpl)} 913 ctxDone, cancellable := ctx.Done().(*channelImpl) 914 cancellationCallback := &receiveCallback{} 915 la := wc.env.ExecuteLocalActivity(*params, func(lar *localActivityResultWrapper) { 916 if cancellable { 917 // future is done, we don't need cancellation anymore 918 ctxDone.removeReceiveCallback(cancellationCallback) 919 } 920 921 if lar.err == nil || IsCanceledError(lar.err) || lar.backoff <= 0 { 922 f.Set(lar.result, lar.err) 923 return 924 } 925 926 // set retry error, and it will be handled by workflow.ExecuteLocalActivity(). 927 f.Set(nil, &needRetryError{Backoff: lar.backoff, Attempt: lar.attempt}) 928 return 929 }) 930 931 if cancellable { 932 cancellationCallback.fn = func(v interface{}, more bool) bool { 933 if ctx.Err() == ErrCanceled { 934 getWorkflowEnvironment(ctx).RequestCancelLocalActivity(la.activityID) 935 } 936 return false 937 } 938 _, ok, more := ctxDone.receiveAsyncImpl(cancellationCallback) 939 if ok || !more { 940 cancellationCallback.fn(nil, more) 941 } 942 } 943 944 return f 945 } 946 947 // ExecuteChildWorkflow requests child workflow execution in the context of a workflow. 948 // Context can be used to pass the settings for the child workflow. 949 // For example: task list that this child workflow should be routed, timeouts that need to be configured. 950 // Use ChildWorkflowOptions to pass down the options. 951 // 952 // cwo := ChildWorkflowOptions{ 953 // ExecutionStartToCloseTimeout: 10 * time.Minute, 954 // TaskStartToCloseTimeout: time.Minute, 955 // } 956 // ctx := WithChildWorkflowOptions(ctx, cwo) 957 // 958 // Input childWorkflow is either a workflow name or a workflow function that is getting scheduled. 959 // Input args are the arguments that need to be passed to the child workflow function represented by childWorkflow. 960 // If the child workflow failed to complete then the future get error would indicate the failure and it can be one of 961 // CustomError, TimeoutError, CanceledError, GenericError. 962 // You can cancel the pending child workflow using context(workflow.WithCancel(ctx)) and that will fail the workflow with 963 // error CanceledError. 964 // ExecuteChildWorkflow returns ChildWorkflowFuture. 965 func ExecuteChildWorkflow(ctx Context, childWorkflow interface{}, args ...interface{}) ChildWorkflowFuture { 966 i := getWorkflowInterceptor(ctx) 967 env := getWorkflowEnvironment(ctx) 968 workflowType := getWorkflowFunctionName(env.GetRegistry(), childWorkflow) 969 return i.ExecuteChildWorkflow(ctx, workflowType, args...) 970 } 971 972 func (wc *workflowEnvironmentInterceptor) ExecuteChildWorkflow(ctx Context, childWorkflowType string, args ...interface{}) ChildWorkflowFuture { 973 mainFuture, mainSettable := newDecodeFuture(ctx, childWorkflowType) 974 executionFuture, executionSettable := NewFuture(ctx) 975 result := &childWorkflowFutureImpl{ 976 decodeFutureImpl: mainFuture.(*decodeFutureImpl), 977 executionFuture: executionFuture.(*futureImpl), 978 } 979 // clients prior to v0.18.4 would incorrectly start child workflows that were started with cancelled contexts, 980 // and did not react to cancellation between requested and started. 981 correctChildCancellation := true 982 workflowOptionsFromCtx := getWorkflowEnvOptions(ctx) 983 984 // Starting with a canceled context should immediately fail, no need to even try. 985 if ctx.Err() != nil { 986 if workflowOptionsFromCtx.bugports.StartChildWorkflowsOnCanceledContext { 987 // backport the bug 988 correctChildCancellation = false 989 } else { 990 mainSettable.SetError(ctx.Err()) 991 executionSettable.SetError(ctx.Err()) 992 return result 993 } 994 } 995 996 dc := workflowOptionsFromCtx.dataConverter 997 env := getWorkflowEnvironment(ctx) 998 wfType, input, err := getValidatedWorkflowFunction(childWorkflowType, args, dc, env.GetRegistry()) 999 if err != nil { 1000 executionSettable.Set(nil, err) 1001 mainSettable.Set(nil, err) 1002 return result 1003 } 1004 options, err := getValidatedWorkflowOptions(ctx) 1005 if err != nil { 1006 executionSettable.Set(nil, err) 1007 mainSettable.Set(nil, err) 1008 return result 1009 } 1010 options.dataConverter = dc 1011 options.contextPropagators = workflowOptionsFromCtx.contextPropagators 1012 options.memo = workflowOptionsFromCtx.memo 1013 options.searchAttributes = workflowOptionsFromCtx.searchAttributes 1014 1015 params := executeWorkflowParams{ 1016 workflowOptions: *options, 1017 input: input, 1018 workflowType: wfType, 1019 header: getWorkflowHeader(ctx, options.contextPropagators), 1020 scheduledTime: Now(ctx), /* this is needed for test framework, and is not send to server */ 1021 } 1022 1023 var childWorkflowExecution *WorkflowExecution 1024 1025 ctxDone, cancellable := ctx.Done().(*channelImpl) 1026 cancellationCallback := &receiveCallback{} 1027 shouldCancelAsync := false 1028 err = getWorkflowEnvironment(ctx).ExecuteChildWorkflow(params, func(r []byte, e error) { 1029 mainSettable.Set(r, e) 1030 if cancellable { 1031 // future is done, we don't need cancellation anymore 1032 ctxDone.removeReceiveCallback(cancellationCallback) 1033 } 1034 }, func(r WorkflowExecution, e error) { 1035 if e == nil { 1036 childWorkflowExecution = &r 1037 } 1038 executionSettable.Set(r, e) 1039 1040 // forward the delayed cancellation if necessary 1041 if shouldCancelAsync && e == nil && !mainFuture.IsReady() { 1042 if workflowOptionsFromCtx.bugports.StartChildWorkflowsOnCanceledContext { 1043 // do nothing: buggy behavior did not forward the cancellation 1044 } else { 1045 getWorkflowEnvironment(ctx).RequestCancelChildWorkflow(*options.domain, childWorkflowExecution.ID) 1046 } 1047 } 1048 }) 1049 1050 if err != nil { 1051 executionSettable.Set(nil, err) 1052 mainSettable.Set(nil, err) 1053 return result 1054 } 1055 1056 if cancellable { 1057 cancellationCallback.fn = func(v interface{}, more bool) bool { 1058 if ctx.Err() == ErrCanceled { 1059 if childWorkflowExecution != nil && !mainFuture.IsReady() { 1060 // child workflow started, and ctx cancelled. forward cancel to the child. 1061 getWorkflowEnvironment(ctx).RequestCancelChildWorkflow(*options.domain, childWorkflowExecution.ID) 1062 } else if childWorkflowExecution == nil && correctChildCancellation { 1063 // decision to start the child has been made, but it has not yet started. 1064 1065 // TODO: ideal, but not strictly necessary for correctness: 1066 // if it's in the same decision, revoke that cancel synchronously. 1067 1068 // if the decision has already gone through: wait for it to be started, and then cancel it. 1069 shouldCancelAsync = true 1070 } 1071 } 1072 return false 1073 } 1074 _, ok, more := ctxDone.receiveAsyncImpl(cancellationCallback) 1075 if ok || !more { 1076 cancellationCallback.fn(nil, more) 1077 } 1078 } 1079 1080 return result 1081 } 1082 1083 func getWorkflowHeader(ctx Context, ctxProps []ContextPropagator) *s.Header { 1084 header := &s.Header{ 1085 Fields: make(map[string][]byte), 1086 } 1087 writer := NewHeaderWriter(header) 1088 for _, ctxProp := range ctxProps { 1089 ctxProp.InjectFromWorkflow(ctx, writer) 1090 } 1091 return header 1092 } 1093 1094 // WorkflowInfo information about currently executing workflow 1095 type WorkflowInfo struct { 1096 WorkflowExecution WorkflowExecution 1097 OriginalRunId string // The original runID before resetting. Using it instead of current runID can make workflow decision determinstic after reset 1098 WorkflowType WorkflowType 1099 TaskListName string 1100 ExecutionStartToCloseTimeoutSeconds int32 1101 TaskStartToCloseTimeoutSeconds int32 1102 Domain string 1103 Attempt int32 // Attempt starts from 0 and increased by 1 for every retry if retry policy is specified. 1104 lastCompletionResult []byte 1105 CronSchedule *string 1106 ContinuedExecutionRunID *string 1107 ParentWorkflowDomain *string 1108 ParentWorkflowExecution *WorkflowExecution 1109 Memo *s.Memo // Value can be decoded using data converter (DefaultDataConverter, or custom one if set). 1110 SearchAttributes *s.SearchAttributes // Value can be decoded using DefaultDataConverter. 1111 BinaryChecksum *string // The identifier(generated by md5sum by default) of worker code that is making the current decision(can be used for auto-reset feature) 1112 DecisionStartedEventID int64 // the eventID of DecisionStarted that is making the current decision(can be used for reset API) 1113 RetryPolicy *s.RetryPolicy 1114 TotalHistoryBytes int64 1115 HistoryBytesServer int64 1116 HistoryCount int64 1117 } 1118 1119 // GetBinaryChecksum returns the binary checksum(identifier) of this worker 1120 // It is the identifier(generated by md5sum by default) of worker code that is making the current decision(can be used for auto-reset feature) 1121 // In replay mode, it's from DecisionTaskCompleted event. In non-replay mode, it's from the currently executing worker. 1122 func (wInfo *WorkflowInfo) GetBinaryChecksum() string { 1123 if wInfo.BinaryChecksum == nil { 1124 return getBinaryChecksum() 1125 } 1126 return *wInfo.BinaryChecksum 1127 } 1128 1129 // GetDecisionCompletedEventID returns the eventID of DecisionStartedEvent that is making the current decision(can be used for reset API: decisionFinishEventID = DecisionStartedEventID + 1) 1130 func (wInfo *WorkflowInfo) GetDecisionStartedEventID() int64 { 1131 return wInfo.DecisionStartedEventID 1132 } 1133 1134 // GetWorkflowInfo extracts info of a current workflow from a context. 1135 func GetWorkflowInfo(ctx Context) *WorkflowInfo { 1136 i := getWorkflowInterceptor(ctx) 1137 return i.GetWorkflowInfo(ctx) 1138 } 1139 1140 func (wc *workflowEnvironmentInterceptor) GetWorkflowInfo(ctx Context) *WorkflowInfo { 1141 return wc.env.WorkflowInfo() 1142 } 1143 1144 // GetLogger returns a logger to be used in workflow's context 1145 func GetLogger(ctx Context) *zap.Logger { 1146 i := getWorkflowInterceptor(ctx) 1147 return i.GetLogger(ctx) 1148 } 1149 1150 func (wc *workflowEnvironmentInterceptor) GetLogger(ctx Context) *zap.Logger { 1151 return wc.env.GetLogger() 1152 } 1153 1154 // GetMetricsScope returns a metrics scope to be used in workflow's context 1155 func GetMetricsScope(ctx Context) tally.Scope { 1156 i := getWorkflowInterceptor(ctx) 1157 return i.GetMetricsScope(ctx) 1158 } 1159 1160 func (wc *workflowEnvironmentInterceptor) GetMetricsScope(ctx Context) tally.Scope { 1161 return wc.env.GetMetricsScope() 1162 } 1163 1164 // GetTotalEstimatedHistoryBytes returns the current history size of that workflow 1165 func GetTotalEstimatedHistoryBytes(ctx Context) int64 { 1166 i := getWorkflowInterceptor(ctx) 1167 return i.GetWorkflowInfo(ctx).TotalHistoryBytes 1168 } 1169 1170 // GetHistoryCount returns the current number of history events of that workflow 1171 func GetHistoryCount(ctx Context) int64 { 1172 i := getWorkflowInterceptor(ctx) 1173 return i.GetWorkflowInfo(ctx).HistoryCount 1174 } 1175 1176 // Now returns the current time in UTC. It corresponds to the time when the decision task is started or replayed. 1177 // Workflow needs to use this method to get the wall clock time instead of the one from the golang library. 1178 func Now(ctx Context) time.Time { 1179 i := getWorkflowInterceptor(ctx) 1180 return i.Now(ctx).UTC() 1181 } 1182 1183 func (wc *workflowEnvironmentInterceptor) Now(ctx Context) time.Time { 1184 return wc.env.Now() 1185 } 1186 1187 // NewTimer returns immediately and the future becomes ready after the specified duration d. The workflow needs to use 1188 // this NewTimer() to get the timer instead of the Go lang library one(timer.NewTimer()). You can cancel the pending 1189 // timer by cancel the Context (using context from workflow.WithCancel(ctx)) and that will cancel the timer. After timer 1190 // is canceled, the returned Future become ready, and Future.Get() will return *CanceledError. 1191 // The current timer resolution implementation is in seconds and uses math.Ceil(d.Seconds()) as the duration. But is 1192 // subjected to change in the future. 1193 func NewTimer(ctx Context, d time.Duration) Future { 1194 i := getWorkflowInterceptor(ctx) 1195 return i.NewTimer(ctx, d) 1196 } 1197 1198 func (wc *workflowEnvironmentInterceptor) NewTimer(ctx Context, d time.Duration) Future { 1199 future, settable := NewFuture(ctx) 1200 if d <= 0 { 1201 settable.Set(true, nil) 1202 return future 1203 } 1204 1205 ctxDone, cancellable := ctx.Done().(*channelImpl) 1206 cancellationCallback := &receiveCallback{} 1207 t := wc.env.NewTimer(d, func(r []byte, e error) { 1208 settable.Set(nil, e) 1209 if cancellable { 1210 // future is done, we don't need cancellation anymore 1211 ctxDone.removeReceiveCallback(cancellationCallback) 1212 } 1213 }) 1214 1215 if t != nil && cancellable { 1216 cancellationCallback.fn = func(v interface{}, more bool) bool { 1217 if !future.IsReady() { 1218 wc.env.RequestCancelTimer(t.timerID) 1219 } 1220 return false 1221 } 1222 _, ok, more := ctxDone.receiveAsyncImpl(cancellationCallback) 1223 if ok || !more { 1224 cancellationCallback.fn(nil, more) 1225 } 1226 } 1227 return future 1228 } 1229 1230 // Sleep pauses the current workflow for at least the duration d. A negative or zero duration causes Sleep to return 1231 // immediately. Workflow code needs to use this Sleep() to sleep instead of the Go lang library one(timer.Sleep()). 1232 // You can cancel the pending sleep by cancel the Context (using context from workflow.WithCancel(ctx)). 1233 // Sleep() returns nil if the duration d is passed, or it returns *CanceledError if the ctx is canceled. There are 2 1234 // reasons the ctx could be canceled: 1) your workflow code cancel the ctx (with workflow.WithCancel(ctx)); 1235 // 2) your workflow itself is canceled by external request. 1236 // The current timer resolution implementation is in seconds and uses math.Ceil(d.Seconds()) as the duration. But is 1237 // subjected to change in the future. 1238 func Sleep(ctx Context, d time.Duration) (err error) { 1239 i := getWorkflowInterceptor(ctx) 1240 return i.Sleep(ctx, d) 1241 } 1242 1243 func (wc *workflowEnvironmentInterceptor) Sleep(ctx Context, d time.Duration) (err error) { 1244 t := NewTimer(ctx, d) 1245 err = t.Get(ctx, nil) 1246 return 1247 } 1248 1249 // RequestCancelExternalWorkflow can be used to request cancellation of an external workflow. 1250 // Input workflowID is the workflow ID of target workflow. 1251 // Input runID indicates the instance of a workflow. Input runID is optional (default is ""). When runID is not specified, 1252 // then the currently running instance of that workflowID will be used. 1253 // By default, the current workflow's domain will be used as target domain. However, you can specify a different domain 1254 // of the target workflow using the context like: 1255 // 1256 // ctx := WithWorkflowDomain(ctx, "domain-name") 1257 // 1258 // RequestCancelExternalWorkflow return Future with failure or empty success result. 1259 func RequestCancelExternalWorkflow(ctx Context, workflowID, runID string) Future { 1260 i := getWorkflowInterceptor(ctx) 1261 return i.RequestCancelExternalWorkflow(ctx, workflowID, runID) 1262 } 1263 1264 func (wc *workflowEnvironmentInterceptor) RequestCancelExternalWorkflow(ctx Context, workflowID, runID string) Future { 1265 ctx1 := setWorkflowEnvOptionsIfNotExist(ctx) 1266 options := getWorkflowEnvOptions(ctx1) 1267 future, settable := NewFuture(ctx1) 1268 1269 if options.domain == nil || *options.domain == "" { 1270 settable.Set(nil, errDomainNotSet) 1271 return future 1272 } 1273 1274 if workflowID == "" { 1275 settable.Set(nil, errWorkflowIDNotSet) 1276 return future 1277 } 1278 1279 resultCallback := func(result []byte, err error) { 1280 settable.Set(result, err) 1281 } 1282 1283 wc.env.RequestCancelExternalWorkflow( 1284 *options.domain, 1285 workflowID, 1286 runID, 1287 resultCallback, 1288 ) 1289 1290 return future 1291 } 1292 1293 // SignalExternalWorkflow can be used to send signal info to an external workflow. 1294 // Input workflowID is the workflow ID of target workflow. 1295 // Input runID indicates the instance of a workflow. Input runID is optional (default is ""). When runID is not specified, 1296 // then the currently running instance of that workflowID will be used. 1297 // By default, the current workflow's domain will be used as target domain. However, you can specify a different domain 1298 // of the target workflow using the context like: 1299 // 1300 // ctx := WithWorkflowDomain(ctx, "domain-name") 1301 // 1302 // SignalExternalWorkflow return Future with failure or empty success result. 1303 func SignalExternalWorkflow(ctx Context, workflowID, runID, signalName string, arg interface{}) Future { 1304 i := getWorkflowInterceptor(ctx) 1305 return i.SignalExternalWorkflow(ctx, workflowID, runID, signalName, arg) 1306 } 1307 1308 func (wc *workflowEnvironmentInterceptor) SignalExternalWorkflow(ctx Context, workflowID, runID, signalName string, arg interface{}) Future { 1309 const childWorkflowOnly = false // this means we are not limited to child workflow 1310 return signalExternalWorkflow(ctx, workflowID, runID, signalName, arg, childWorkflowOnly) 1311 } 1312 1313 func signalExternalWorkflow(ctx Context, workflowID, runID, signalName string, arg interface{}, childWorkflowOnly bool) Future { 1314 env := getWorkflowEnvironment(ctx) 1315 ctx1 := setWorkflowEnvOptionsIfNotExist(ctx) 1316 options := getWorkflowEnvOptions(ctx1) 1317 future, settable := NewFuture(ctx1) 1318 1319 if options.domain == nil || *options.domain == "" { 1320 settable.Set(nil, errDomainNotSet) 1321 return future 1322 } 1323 1324 if workflowID == "" { 1325 settable.Set(nil, errWorkflowIDNotSet) 1326 return future 1327 } 1328 1329 input, err := encodeArg(options.dataConverter, arg) 1330 if err != nil { 1331 settable.Set(nil, err) 1332 return future 1333 } 1334 1335 resultCallback := func(result []byte, err error) { 1336 settable.Set(result, err) 1337 } 1338 env.SignalExternalWorkflow( 1339 *options.domain, 1340 workflowID, 1341 runID, 1342 signalName, 1343 input, 1344 arg, 1345 childWorkflowOnly, 1346 resultCallback, 1347 ) 1348 1349 return future 1350 } 1351 1352 // UpsertSearchAttributes is used to add or update workflow search attributes. 1353 // The search attributes can be used in query of List/Scan/Count workflow APIs. 1354 // The key and value type must be registered on cadence server side; 1355 // The value has to deterministic when replay; 1356 // The value has to be Json serializable. 1357 // UpsertSearchAttributes will merge attributes to existing map in workflow, for example workflow code: 1358 // 1359 // func MyWorkflow(ctx workflow.Context, input string) error { 1360 // attr1 := map[string]interface{}{ 1361 // "CustomIntField": 1, 1362 // "CustomBoolField": true, 1363 // } 1364 // workflow.UpsertSearchAttributes(ctx, attr1) 1365 // 1366 // attr2 := map[string]interface{}{ 1367 // "CustomIntField": 2, 1368 // "CustomKeywordField": "seattle", 1369 // } 1370 // workflow.UpsertSearchAttributes(ctx, attr2) 1371 // } 1372 // 1373 // will eventually have search attributes: 1374 // 1375 // map[string]interface{}{ 1376 // "CustomIntField": 2, 1377 // "CustomBoolField": true, 1378 // "CustomKeywordField": "seattle", 1379 // } 1380 // 1381 // This is only supported when using ElasticSearch. 1382 func UpsertSearchAttributes(ctx Context, attributes map[string]interface{}) error { 1383 i := getWorkflowInterceptor(ctx) 1384 return i.UpsertSearchAttributes(ctx, attributes) 1385 } 1386 1387 func (wc *workflowEnvironmentInterceptor) UpsertSearchAttributes(ctx Context, attributes map[string]interface{}) error { 1388 if _, ok := attributes[CadenceChangeVersion]; ok { 1389 return errors.New("CadenceChangeVersion is a reserved key that cannot be set, please use other key") 1390 } 1391 return wc.env.UpsertSearchAttributes(attributes) 1392 } 1393 1394 // WithChildWorkflowOptions adds all workflow options to the context. 1395 // The current timeout resolution implementation is in seconds and uses math.Ceil(d.Seconds()) as the duration. But is 1396 // subjected to change in the future. 1397 func WithChildWorkflowOptions(ctx Context, cwo ChildWorkflowOptions) Context { 1398 ctx1 := setWorkflowEnvOptionsIfNotExist(ctx) 1399 wfOptions := getWorkflowEnvOptions(ctx1) 1400 wfOptions.domain = common.StringPtr(cwo.Domain) 1401 wfOptions.taskListName = common.StringPtr(cwo.TaskList) 1402 wfOptions.workflowID = cwo.WorkflowID 1403 wfOptions.executionStartToCloseTimeoutSeconds = common.Int32Ptr(common.Int32Ceil(cwo.ExecutionStartToCloseTimeout.Seconds())) 1404 wfOptions.taskStartToCloseTimeoutSeconds = common.Int32Ptr(common.Int32Ceil(cwo.TaskStartToCloseTimeout.Seconds())) 1405 wfOptions.waitForCancellation = cwo.WaitForCancellation 1406 wfOptions.workflowIDReusePolicy = cwo.WorkflowIDReusePolicy 1407 wfOptions.retryPolicy = convertRetryPolicy(cwo.RetryPolicy) 1408 wfOptions.cronSchedule = cwo.CronSchedule 1409 wfOptions.memo = cwo.Memo 1410 wfOptions.searchAttributes = cwo.SearchAttributes 1411 wfOptions.parentClosePolicy = cwo.ParentClosePolicy 1412 wfOptions.bugports = cwo.Bugports 1413 1414 return ctx1 1415 } 1416 1417 // WithWorkflowDomain adds a domain to the context. 1418 func WithWorkflowDomain(ctx Context, name string) Context { 1419 ctx1 := setWorkflowEnvOptionsIfNotExist(ctx) 1420 getWorkflowEnvOptions(ctx1).domain = common.StringPtr(name) 1421 return ctx1 1422 } 1423 1424 // WithWorkflowTaskList adds a task list to the context. 1425 func WithWorkflowTaskList(ctx Context, name string) Context { 1426 ctx1 := setWorkflowEnvOptionsIfNotExist(ctx) 1427 getWorkflowEnvOptions(ctx1).taskListName = common.StringPtr(name) 1428 return ctx1 1429 } 1430 1431 // GetWorkflowTaskList retrieves current workflow tasklist from context 1432 func GetWorkflowTaskList(ctx Context) *string { 1433 wo := getWorkflowEnvOptions(ctx) 1434 if wo == nil || wo.taskListName == nil { 1435 return nil 1436 } 1437 tl := *wo.taskListName // copy 1438 return &tl 1439 } 1440 1441 // WithWorkflowID adds a workflowID to the context. 1442 func WithWorkflowID(ctx Context, workflowID string) Context { 1443 ctx1 := setWorkflowEnvOptionsIfNotExist(ctx) 1444 getWorkflowEnvOptions(ctx1).workflowID = workflowID 1445 return ctx1 1446 } 1447 1448 // WithExecutionStartToCloseTimeout adds a workflow execution timeout to the context. 1449 // The current timeout resolution implementation is in seconds and uses math.Ceil(d.Seconds()) as the duration. But is 1450 // subjected to change in the future. 1451 func WithExecutionStartToCloseTimeout(ctx Context, d time.Duration) Context { 1452 ctx1 := setWorkflowEnvOptionsIfNotExist(ctx) 1453 getWorkflowEnvOptions(ctx1).executionStartToCloseTimeoutSeconds = common.Int32Ptr(common.Int32Ceil(d.Seconds())) 1454 return ctx1 1455 } 1456 1457 // WithWorkflowTaskStartToCloseTimeout adds a decision timeout to the context. 1458 // The current timeout resolution implementation is in seconds and uses math.Ceil(d.Seconds()) as the duration. But is 1459 // subjected to change in the future. 1460 func WithWorkflowTaskStartToCloseTimeout(ctx Context, d time.Duration) Context { 1461 ctx1 := setWorkflowEnvOptionsIfNotExist(ctx) 1462 getWorkflowEnvOptions(ctx1).taskStartToCloseTimeoutSeconds = common.Int32Ptr(common.Int32Ceil(d.Seconds())) 1463 return ctx1 1464 } 1465 1466 // WithDataConverter adds DataConverter to the context. 1467 func WithDataConverter(ctx Context, dc DataConverter) Context { 1468 if dc == nil { 1469 panic("data converter is nil for WithDataConverter") 1470 } 1471 ctx1 := setWorkflowEnvOptionsIfNotExist(ctx) 1472 getWorkflowEnvOptions(ctx1).dataConverter = dc 1473 return ctx1 1474 } 1475 1476 // withContextPropagators adds ContextPropagators to the context. 1477 func withContextPropagators(ctx Context, contextPropagators []ContextPropagator) Context { 1478 ctx1 := setWorkflowEnvOptionsIfNotExist(ctx) 1479 getWorkflowEnvOptions(ctx1).contextPropagators = contextPropagators 1480 return ctx1 1481 } 1482 1483 // GetSignalChannel returns channel corresponding to the signal name. 1484 func GetSignalChannel(ctx Context, signalName string) Channel { 1485 i := getWorkflowInterceptor(ctx) 1486 return i.GetSignalChannel(ctx, signalName) 1487 } 1488 1489 func (wc *workflowEnvironmentInterceptor) GetSignalChannel(ctx Context, signalName string) Channel { 1490 return getWorkflowEnvOptions(ctx).getSignalChannel(ctx, signalName) 1491 } 1492 1493 func newEncodedValue(value []byte, dc DataConverter) Value { 1494 if dc == nil { 1495 dc = getDefaultDataConverter() 1496 } 1497 return &EncodedValue{value, dc} 1498 } 1499 1500 // Get extract data from encoded data to desired value type. valuePtr is pointer to the actual value type. 1501 func (b EncodedValue) Get(valuePtr interface{}) error { 1502 if !b.HasValue() { 1503 return ErrNoData 1504 } 1505 return decodeArg(b.dataConverter, b.value, valuePtr) 1506 } 1507 1508 // HasValue return whether there is value 1509 func (b EncodedValue) HasValue() bool { 1510 return b.value != nil 1511 } 1512 1513 // SideEffect executes the provided function once, records its result into the workflow history. The recorded result on 1514 // history will be returned without executing the provided function during replay. This guarantees the deterministic 1515 // requirement for workflow as the exact same result will be returned in replay. 1516 // Common use case is to run some short non-deterministic code in workflow, like getting random number or new UUID. 1517 // The only way to fail SideEffect is to panic which causes decision task failure. The decision task after timeout is 1518 // rescheduled and re-executed giving SideEffect another chance to succeed. 1519 // 1520 // Caution: do not use SideEffect to modify closures. Always retrieve result from SideEffect's encoded return value. 1521 // For example this code is BROKEN: 1522 // 1523 // // Bad example: 1524 // var random int 1525 // workflow.SideEffect(func(ctx workflow.Context) interface{} { 1526 // random = rand.Intn(100) 1527 // return nil 1528 // }) 1529 // // random will always be 0 in replay, thus this code is non-deterministic 1530 // if random < 50 { 1531 // .... 1532 // } else { 1533 // .... 1534 // } 1535 // 1536 // On replay the provided function is not executed, the random will always be 0, and the workflow could takes a 1537 // different path breaking the determinism. 1538 // 1539 // Here is the correct way to use SideEffect: 1540 // 1541 // // Good example: 1542 // encodedRandom := SideEffect(func(ctx workflow.Context) interface{} { 1543 // return rand.Intn(100) 1544 // }) 1545 // var random int 1546 // encodedRandom.Get(&random) 1547 // if random < 50 { 1548 // .... 1549 // } else { 1550 // .... 1551 // } 1552 func SideEffect(ctx Context, f func(ctx Context) interface{}) Value { 1553 i := getWorkflowInterceptor(ctx) 1554 return i.SideEffect(ctx, f) 1555 } 1556 1557 func (wc *workflowEnvironmentInterceptor) SideEffect(ctx Context, f func(ctx Context) interface{}) Value { 1558 dc := getDataConverterFromWorkflowContext(ctx) 1559 future, settable := NewFuture(ctx) 1560 wrapperFunc := func() ([]byte, error) { 1561 r := f(ctx) 1562 return encodeArg(dc, r) 1563 } 1564 resultCallback := func(result []byte, err error) { 1565 settable.Set(EncodedValue{result, dc}, err) 1566 } 1567 wc.env.SideEffect(wrapperFunc, resultCallback) 1568 var encoded EncodedValue 1569 if err := future.Get(ctx, &encoded); err != nil { 1570 panic(err) 1571 } 1572 return encoded 1573 } 1574 1575 // MutableSideEffect executes the provided function once, then it looks up the history for the value with the given id. 1576 // If there is no existing value, then it records the function result as a value with the given id on history; 1577 // otherwise, it compares whether the existing value from history has changed from the new function result by calling the 1578 // provided equals function. If they are equal, it returns the value without recording a new one in history; 1579 // 1580 // otherwise, it records the new value with the same id on history. 1581 // 1582 // Caution: do not use MutableSideEffect to modify closures. Always retrieve result from MutableSideEffect's encoded 1583 // return value. 1584 // 1585 // The difference between MutableSideEffect() and SideEffect() is that every new SideEffect() call in non-replay will 1586 // result in a new marker being recorded on history. However, MutableSideEffect() only records a new marker if the value 1587 // changed. During replay, MutableSideEffect() will not execute the function again, but it will return the exact same 1588 // value as it was returning during the non-replay run. 1589 // 1590 // One good use case of MutableSideEffect() is to access dynamically changing config without breaking determinism. 1591 func MutableSideEffect(ctx Context, id string, f func(ctx Context) interface{}, equals func(a, b interface{}) bool) Value { 1592 i := getWorkflowInterceptor(ctx) 1593 return i.MutableSideEffect(ctx, id, f, equals) 1594 } 1595 1596 func (wc *workflowEnvironmentInterceptor) MutableSideEffect(ctx Context, id string, f func(ctx Context) interface{}, equals func(a, b interface{}) bool) Value { 1597 wrapperFunc := func() interface{} { 1598 return f(ctx) 1599 } 1600 return wc.env.MutableSideEffect(id, wrapperFunc, equals) 1601 } 1602 1603 // DefaultVersion is a version returned by GetVersion for code that wasn't versioned before 1604 const DefaultVersion Version = -1 1605 1606 // CadenceChangeVersion is used as search attributes key to find workflows with specific change version. 1607 const CadenceChangeVersion = "CadenceChangeVersion" 1608 1609 // GetVersion is used to safely perform backwards incompatible changes to workflow definitions. 1610 // It is not allowed to update workflow code while there are workflows running as it is going to break 1611 // determinism. The solution is to have both old code that is used to replay existing workflows 1612 // as well as the new one that is used when it is executed for the first time. 1613 // GetVersion returns maxSupported version when is executed for the first time. This version is recorded into the 1614 // workflow history as a marker event. Even if maxSupported version is changed the version that was recorded is 1615 // returned on replay. DefaultVersion constant contains version of code that wasn't versioned before. 1616 // For example initially workflow has the following code: 1617 // 1618 // err = workflow.ExecuteActivity(ctx, foo).Get(ctx, nil) 1619 // 1620 // it should be updated to 1621 // 1622 // err = workflow.ExecuteActivity(ctx, bar).Get(ctx, nil) 1623 // 1624 // The backwards compatible way to execute the update is 1625 // 1626 // v := GetVersion(ctx, "fooChange", DefaultVersion, 1) 1627 // if v == DefaultVersion { 1628 // err = workflow.ExecuteActivity(ctx, foo).Get(ctx, nil) 1629 // } else { 1630 // err = workflow.ExecuteActivity(ctx, bar).Get(ctx, nil) 1631 // } 1632 // 1633 // Then bar has to be changed to baz: 1634 // 1635 // v := GetVersion(ctx, "fooChange", DefaultVersion, 2) 1636 // if v == DefaultVersion { 1637 // err = workflow.ExecuteActivity(ctx, foo).Get(ctx, nil) 1638 // } else if v == 1 { 1639 // err = workflow.ExecuteActivity(ctx, bar).Get(ctx, nil) 1640 // } else { 1641 // err = workflow.ExecuteActivity(ctx, baz).Get(ctx, nil) 1642 // } 1643 // 1644 // Later when there are no workflow executions running DefaultVersion the correspondent branch can be removed: 1645 // 1646 // v := GetVersion(ctx, "fooChange", 1, 2) 1647 // if v == 1 { 1648 // err = workflow.ExecuteActivity(ctx, bar).Get(ctx, nil) 1649 // } else { 1650 // err = workflow.ExecuteActivity(ctx, baz).Get(ctx, nil) 1651 // } 1652 // 1653 // It is recommended to keep the GetVersion() call even if single branch is left: 1654 // 1655 // GetVersion(ctx, "fooChange", 2, 2) 1656 // err = workflow.ExecuteActivity(ctx, baz).Get(ctx, nil) 1657 // 1658 // The reason to keep it is: 1) it ensures that if there is older version execution still running, it will fail here 1659 // and not proceed; 2) if you ever need to make more changes for “fooChange”, for example change activity from baz to qux, 1660 // you just need to update the maxVersion from 2 to 3. 1661 // 1662 // Note that, you only need to preserve the first call to GetVersion() for each changeID. All subsequent call to GetVersion() 1663 // with same changeID are safe to remove. However, if you really want to get rid of the first GetVersion() call as well, 1664 // you can do so, but you need to make sure: 1) all older version executions are completed; 2) you can no longer use “fooChange” 1665 // as changeID. If you ever need to make changes to that same part like change from baz to qux, you would need to use a 1666 // different changeID like “fooChange-fix2”, and start minVersion from DefaultVersion again. The code would looks like: 1667 // 1668 // v := workflow.GetVersion(ctx, "fooChange-fix2", workflow.DefaultVersion, 1) 1669 // if v == workflow.DefaultVersion { 1670 // err = workflow.ExecuteActivity(ctx, baz, data).Get(ctx, nil) 1671 // } else { 1672 // err = workflow.ExecuteActivity(ctx, qux, data).Get(ctx, nil) 1673 // } 1674 func GetVersion(ctx Context, changeID string, minSupported, maxSupported Version) Version { 1675 i := getWorkflowInterceptor(ctx) 1676 return i.GetVersion(ctx, changeID, minSupported, maxSupported) 1677 } 1678 1679 func (wc *workflowEnvironmentInterceptor) GetVersion(ctx Context, changeID string, minSupported, maxSupported Version) Version { 1680 return wc.env.GetVersion(changeID, minSupported, maxSupported) 1681 } 1682 1683 // SetQueryHandler sets the query handler to handle workflow query. The queryType specify which query type this handler 1684 // should handle. The handler must be a function that returns 2 values. The first return value must be a serializable 1685 // result. The second return value must be an error. The handler function could receive any number of input parameters. 1686 // All the input parameter must be serializable. You should call workflow.SetQueryHandler() at the beginning of the workflow 1687 // code. When client calls Client.QueryWorkflow() to cadence server, a task will be generated on server that will be dispatched 1688 // to a workflow worker, which will replay the history events and then execute a query handler based on the query type. 1689 // The query handler will be invoked out of the context of the workflow, meaning that the handler code must not use cadence 1690 // context to do things like workflow.NewChannel(), workflow.Go() or to call any workflow blocking functions like 1691 // Channel.Get() or Future.Get(). Trying to do so in query handler code will fail the query and client will receive 1692 // QueryFailedError. 1693 // Example of workflow code that support query type "current_state": 1694 // 1695 // func MyWorkflow(ctx workflow.Context, input string) error { 1696 // currentState := "started" // this could be any serializable struct 1697 // err := workflow.SetQueryHandler(ctx, "current_state", func() (string, error) { 1698 // return currentState, nil 1699 // }) 1700 // if err != nil { 1701 // currentState = "failed to register query handler" 1702 // return err 1703 // } 1704 // // your normal workflow code begins here, and you update the currentState as the code makes progress. 1705 // currentState = "waiting timer" 1706 // err = NewTimer(ctx, time.Hour).Get(ctx, nil) 1707 // if err != nil { 1708 // currentState = "timer failed" 1709 // return err 1710 // } 1711 // 1712 // currentState = "waiting activity" 1713 // ctx = WithActivityOptions(ctx, myActivityOptions) 1714 // err = ExecuteActivity(ctx, MyActivity, "my_input").Get(ctx, nil) 1715 // if err != nil { 1716 // currentState = "activity failed" 1717 // return err 1718 // } 1719 // currentState = "done" 1720 // return nil 1721 // } 1722 func SetQueryHandler(ctx Context, queryType string, handler interface{}) error { 1723 i := getWorkflowInterceptor(ctx) 1724 return i.SetQueryHandler(ctx, queryType, handler) 1725 } 1726 1727 func (wc *workflowEnvironmentInterceptor) SetQueryHandler(ctx Context, queryType string, handler interface{}) error { 1728 if strings.HasPrefix(queryType, "__") { 1729 return errors.New("queryType starts with '__' is reserved for internal use") 1730 } 1731 return setQueryHandler(ctx, queryType, handler) 1732 } 1733 1734 // IsReplaying returns whether the current workflow code is replaying. 1735 // 1736 // Warning! Never make decisions, like schedule activity/childWorkflow/timer or send/wait on future/channel, based on 1737 // this flag as it is going to break workflow determinism requirement. 1738 // The only reasonable use case for this flag is to avoid some external actions during replay, like custom logging or 1739 // metric reporting. Please note that Cadence already provide standard logging/metric via workflow.GetLogger(ctx) and 1740 // workflow.GetMetricsScope(ctx), and those standard mechanism are replay-aware and it will automatically suppress during 1741 // replay. Only use this flag if you need custom logging/metrics reporting, for example if you want to log to kafka. 1742 // 1743 // Warning! Any action protected by this flag should not fail or if it does fail should ignore that failure or panic 1744 // on the failure. If workflow don't want to be blocked on those failure, it should ignore those failure; if workflow do 1745 // want to make sure it proceed only when that action succeed then it should panic on that failure. Panic raised from a 1746 // workflow causes decision task to fail and cadence server will rescheduled later to retry. 1747 func IsReplaying(ctx Context) bool { 1748 i := getWorkflowInterceptor(ctx) 1749 return i.IsReplaying(ctx) 1750 } 1751 1752 func (wc *workflowEnvironmentInterceptor) IsReplaying(ctx Context) bool { 1753 return wc.env.IsReplaying() 1754 } 1755 1756 // HasLastCompletionResult checks if there is completion result from previous runs. 1757 // This is used in combination with cron schedule. A workflow can be started with an optional cron schedule. 1758 // If a cron workflow wants to pass some data to next schedule, it can return any data and that data will become 1759 // available when next run starts. 1760 // This HasLastCompletionResult() checks if there is such data available passing down from previous successful run. 1761 func HasLastCompletionResult(ctx Context) bool { 1762 i := getWorkflowInterceptor(ctx) 1763 return i.HasLastCompletionResult(ctx) 1764 } 1765 1766 func (wc *workflowEnvironmentInterceptor) HasLastCompletionResult(ctx Context) bool { 1767 info := wc.GetWorkflowInfo(ctx) 1768 return len(info.lastCompletionResult) > 0 1769 } 1770 1771 // GetLastCompletionResult extract last completion result from previous run for this cron workflow. 1772 // This is used in combination with cron schedule. A workflow can be started with an optional cron schedule. 1773 // If a cron workflow wants to pass some data to next schedule, it can return any data and that data will become 1774 // available when next run starts. 1775 // This GetLastCompletionResult() extract the data into expected data structure. 1776 func GetLastCompletionResult(ctx Context, d ...interface{}) error { 1777 i := getWorkflowInterceptor(ctx) 1778 return i.GetLastCompletionResult(ctx, d...) 1779 } 1780 1781 func (wc *workflowEnvironmentInterceptor) GetLastCompletionResult(ctx Context, d ...interface{}) error { 1782 info := wc.GetWorkflowInfo(ctx) 1783 if len(info.lastCompletionResult) == 0 { 1784 return ErrNoData 1785 } 1786 1787 encodedVal := newEncodedValues(info.lastCompletionResult, getDataConverterFromWorkflowContext(ctx)) 1788 return encodedVal.Get(d...) 1789 } 1790 1791 // WithActivityOptions adds all options to the copy of the context. 1792 // The current timeout resolution implementation is in seconds and uses math.Ceil(d.Seconds()) as the duration. But is 1793 // subjected to change in the future. 1794 func WithActivityOptions(ctx Context, options ActivityOptions) Context { 1795 ctx1 := setActivityParametersIfNotExist(ctx) 1796 eap := getActivityOptions(ctx1) 1797 1798 eap.TaskListName = options.TaskList 1799 eap.ScheduleToCloseTimeoutSeconds = common.Int32Ceil(options.ScheduleToCloseTimeout.Seconds()) 1800 eap.StartToCloseTimeoutSeconds = common.Int32Ceil(options.StartToCloseTimeout.Seconds()) 1801 eap.ScheduleToStartTimeoutSeconds = common.Int32Ceil(options.ScheduleToStartTimeout.Seconds()) 1802 eap.HeartbeatTimeoutSeconds = common.Int32Ceil(options.HeartbeatTimeout.Seconds()) 1803 eap.WaitForCancellation = options.WaitForCancellation 1804 eap.ActivityID = common.StringPtr(options.ActivityID) 1805 eap.RetryPolicy = convertRetryPolicy(options.RetryPolicy) 1806 return ctx1 1807 } 1808 1809 // WithLocalActivityOptions adds local activity options to the copy of the context. 1810 // The current timeout resolution implementation is in seconds and uses math.Ceil(d.Seconds()) as the duration. But is 1811 // subjected to change in the future. 1812 func WithLocalActivityOptions(ctx Context, options LocalActivityOptions) Context { 1813 ctx1 := setLocalActivityParametersIfNotExist(ctx) 1814 opts := getLocalActivityOptions(ctx1) 1815 1816 opts.ScheduleToCloseTimeoutSeconds = common.Int32Ceil(options.ScheduleToCloseTimeout.Seconds()) 1817 opts.RetryPolicy = options.RetryPolicy 1818 return ctx1 1819 } 1820 1821 // WithTaskList adds a task list to the copy of the context. 1822 // Note this shall not confuse with WithWorkflowTaskList. This is the tasklist for activities 1823 func WithTaskList(ctx Context, name string) Context { 1824 ctx1 := setActivityParametersIfNotExist(ctx) 1825 getActivityOptions(ctx1).TaskListName = name 1826 return ctx1 1827 } 1828 1829 // GetActivityTaskList retrieves tasklist info from context 1830 func GetActivityTaskList(ctx Context) *string { 1831 ao := getActivityOptions(ctx) 1832 if ao == nil { 1833 return nil 1834 } 1835 tl := ao.TaskListName // copy 1836 return &tl 1837 } 1838 1839 // WithScheduleToCloseTimeout adds a timeout to the copy of the context. 1840 // The current timeout resolution implementation is in seconds and uses math.Ceil(d.Seconds()) as the duration. But is 1841 // subjected to change in the future. 1842 func WithScheduleToCloseTimeout(ctx Context, d time.Duration) Context { 1843 ctx1 := setActivityParametersIfNotExist(ctx) 1844 getActivityOptions(ctx1).ScheduleToCloseTimeoutSeconds = common.Int32Ceil(d.Seconds()) 1845 return ctx1 1846 } 1847 1848 // WithScheduleToStartTimeout adds a timeout to the copy of the context. 1849 // The current timeout resolution implementation is in seconds and uses math.Ceil(d.Seconds()) as the duration. But is 1850 // subjected to change in the future. 1851 func WithScheduleToStartTimeout(ctx Context, d time.Duration) Context { 1852 ctx1 := setActivityParametersIfNotExist(ctx) 1853 getActivityOptions(ctx1).ScheduleToStartTimeoutSeconds = common.Int32Ceil(d.Seconds()) 1854 return ctx1 1855 } 1856 1857 // WithStartToCloseTimeout adds a timeout to the copy of the context. 1858 // The current timeout resolution implementation is in seconds and uses math.Ceil(d.Seconds()) as the duration. But is 1859 // subjected to change in the future. 1860 func WithStartToCloseTimeout(ctx Context, d time.Duration) Context { 1861 ctx1 := setActivityParametersIfNotExist(ctx) 1862 getActivityOptions(ctx1).StartToCloseTimeoutSeconds = common.Int32Ceil(d.Seconds()) 1863 return ctx1 1864 } 1865 1866 // WithHeartbeatTimeout adds a timeout to the copy of the context. 1867 // The current timeout resolution implementation is in seconds and uses math.Ceil(d.Seconds()) as the duration. But is 1868 // subjected to change in the future. 1869 func WithHeartbeatTimeout(ctx Context, d time.Duration) Context { 1870 ctx1 := setActivityParametersIfNotExist(ctx) 1871 getActivityOptions(ctx1).HeartbeatTimeoutSeconds = common.Int32Ceil(d.Seconds()) 1872 return ctx1 1873 } 1874 1875 // WithWaitForCancellation adds wait for the cacellation to the copy of the context. 1876 func WithWaitForCancellation(ctx Context, wait bool) Context { 1877 ctx1 := setActivityParametersIfNotExist(ctx) 1878 getActivityOptions(ctx1).WaitForCancellation = wait 1879 return ctx1 1880 } 1881 1882 // WithRetryPolicy adds retry policy to the copy of the context 1883 func WithRetryPolicy(ctx Context, retryPolicy RetryPolicy) Context { 1884 ctx1 := setActivityParametersIfNotExist(ctx) 1885 getActivityOptions(ctx1).RetryPolicy = convertRetryPolicy(&retryPolicy) 1886 return ctx1 1887 } 1888 1889 func convertRetryPolicy(retryPolicy *RetryPolicy) *s.RetryPolicy { 1890 if retryPolicy == nil { 1891 return nil 1892 } 1893 if retryPolicy.BackoffCoefficient == 0 { 1894 retryPolicy.BackoffCoefficient = backoff.DefaultBackoffCoefficient 1895 } 1896 thriftRetryPolicy := s.RetryPolicy{ 1897 InitialIntervalInSeconds: common.Int32Ptr(common.Int32Ceil(retryPolicy.InitialInterval.Seconds())), 1898 MaximumIntervalInSeconds: common.Int32Ptr(common.Int32Ceil(retryPolicy.MaximumInterval.Seconds())), 1899 BackoffCoefficient: &retryPolicy.BackoffCoefficient, 1900 MaximumAttempts: &retryPolicy.MaximumAttempts, 1901 NonRetriableErrorReasons: retryPolicy.NonRetriableErrorReasons, 1902 ExpirationIntervalInSeconds: common.Int32Ptr(common.Int32Ceil(retryPolicy.ExpirationInterval.Seconds())), 1903 } 1904 return &thriftRetryPolicy 1905 }