go.uber.org/cadence@v1.2.9/internal/internal_decision_state_machine.go (about) 1 // Copyright (c) 2017 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package internal 22 23 import ( 24 "container/list" 25 "fmt" 26 27 s "go.uber.org/cadence/.gen/go/shared" 28 "go.uber.org/cadence/internal/common" 29 "go.uber.org/cadence/internal/common/util" 30 ) 31 32 type ( 33 decisionState int32 34 decisionType int32 35 36 decisionID struct { 37 decisionType decisionType 38 id string 39 } 40 41 decisionStateMachine interface { 42 getState() decisionState 43 getID() decisionID 44 isDone() bool 45 getDecision() *s.Decision // return nil if there is no decision in current state 46 cancel() 47 48 handleStartedEvent() 49 handleCancelInitiatedEvent() 50 handleCanceledEvent() 51 handleCancelFailedEvent() 52 handleCompletionEvent() 53 handleInitiationFailedEvent() 54 handleInitiatedEvent() 55 56 handleDecisionSent() 57 58 setData(data interface{}) 59 getData() interface{} 60 } 61 62 decisionStateMachineBase struct { 63 id decisionID 64 state decisionState 65 history []string 66 data interface{} 67 helper *decisionsHelper 68 } 69 70 activityDecisionStateMachine struct { 71 *decisionStateMachineBase 72 attributes *s.ScheduleActivityTaskDecisionAttributes 73 } 74 75 timerDecisionStateMachine struct { 76 *decisionStateMachineBase 77 attributes *s.StartTimerDecisionAttributes 78 canceled bool 79 } 80 81 childWorkflowDecisionStateMachine struct { 82 *decisionStateMachineBase 83 attributes *s.StartChildWorkflowExecutionDecisionAttributes 84 } 85 86 naiveDecisionStateMachine struct { 87 *decisionStateMachineBase 88 decision *s.Decision 89 } 90 91 // only possible state transition is: CREATED->SENT->INITIATED->COMPLETED 92 cancelExternalWorkflowDecisionStateMachine struct { 93 *naiveDecisionStateMachine 94 } 95 96 signalExternalWorkflowDecisionStateMachine struct { 97 *naiveDecisionStateMachine 98 } 99 100 // only possible state transition is: CREATED->SENT->COMPLETED 101 markerDecisionStateMachine struct { 102 *naiveDecisionStateMachine 103 } 104 105 upsertSearchAttributesDecisionStateMachine struct { 106 *naiveDecisionStateMachine 107 } 108 109 decisionsHelper struct { 110 orderedDecisions *list.List 111 decisions map[decisionID]*list.Element 112 113 scheduledEventIDToActivityID map[int64]string 114 scheduledEventIDToCancellationID map[int64]string 115 scheduledEventIDToSignalID map[int64]string 116 } 117 118 // panic when decision state machine is in illegal state 119 stateMachineIllegalStatePanic struct { 120 message string 121 } 122 ) 123 124 const ( 125 decisionStateCreated decisionState = 0 126 decisionStateDecisionSent decisionState = 1 127 decisionStateCanceledBeforeInitiated decisionState = 2 128 decisionStateInitiated decisionState = 3 129 decisionStateStarted decisionState = 4 130 decisionStateCanceledAfterInitiated decisionState = 5 131 decisionStateCanceledAfterStarted decisionState = 6 132 decisionStateCancellationDecisionSent decisionState = 7 133 decisionStateCompletedAfterCancellationDecisionSent decisionState = 8 134 decisionStateCompleted decisionState = 9 135 ) 136 137 const ( 138 decisionTypeActivity decisionType = 0 139 decisionTypeChildWorkflow decisionType = 1 140 decisionTypeCancellation decisionType = 2 141 decisionTypeMarker decisionType = 3 142 decisionTypeTimer decisionType = 4 143 decisionTypeSignal decisionType = 5 144 decisionTypeUpsertSearchAttributes decisionType = 6 145 ) 146 147 const ( 148 eventCancel = "cancel" 149 eventDecisionSent = "handleDecisionSent" 150 eventInitiated = "handleInitiatedEvent" 151 eventInitiationFailed = "handleInitiationFailedEvent" 152 eventStarted = "handleStartedEvent" 153 eventCompletion = "handleCompletionEvent" 154 eventCancelInitiated = "handleCancelInitiatedEvent" 155 eventCancelFailed = "handleCancelFailedEvent" 156 eventCanceled = "handleCanceledEvent" 157 ) 158 159 const ( 160 sideEffectMarkerName = "SideEffect" 161 versionMarkerName = "Version" 162 localActivityMarkerName = "LocalActivity" 163 mutableSideEffectMarkerName = "MutableSideEffect" 164 ) 165 166 func (d decisionState) String() string { 167 switch d { 168 case decisionStateCreated: 169 return "Created" 170 case decisionStateDecisionSent: 171 return "DecisionSent" 172 case decisionStateCanceledBeforeInitiated: 173 return "CanceledBeforeInitiated" 174 case decisionStateInitiated: 175 return "Initiated" 176 case decisionStateStarted: 177 return "Started" 178 case decisionStateCanceledAfterInitiated: 179 return "CanceledAfterInitiated" 180 case decisionStateCanceledAfterStarted: 181 return "CanceledAfterStarted" 182 case decisionStateCancellationDecisionSent: 183 return "CancellationDecisionSent" 184 case decisionStateCompletedAfterCancellationDecisionSent: 185 return "CompletedAfterCancellationDecisionSent" 186 case decisionStateCompleted: 187 return "Completed" 188 default: 189 return "Unknown" 190 } 191 } 192 193 func (d decisionType) String() string { 194 switch d { 195 case decisionTypeActivity: 196 return "Activity" 197 case decisionTypeChildWorkflow: 198 return "ChildWorkflow" 199 case decisionTypeCancellation: 200 return "Cancellation" 201 case decisionTypeMarker: 202 return "Marker" 203 case decisionTypeTimer: 204 return "Timer" 205 case decisionTypeSignal: 206 return "Signal" 207 default: 208 return "Unknown" 209 } 210 } 211 212 func (d decisionID) String() string { 213 return fmt.Sprintf("DecisionType: %v, ID: %v", d.decisionType, d.id) 214 } 215 216 func makeDecisionID(decisionType decisionType, id string) decisionID { 217 return decisionID{decisionType: decisionType, id: id} 218 } 219 220 func (h *decisionsHelper) newDecisionStateMachineBase(decisionType decisionType, id string) *decisionStateMachineBase { 221 return &decisionStateMachineBase{ 222 id: makeDecisionID(decisionType, id), 223 state: decisionStateCreated, 224 history: []string{decisionStateCreated.String()}, 225 helper: h, 226 } 227 } 228 229 func (h *decisionsHelper) newActivityDecisionStateMachine(attributes *s.ScheduleActivityTaskDecisionAttributes) *activityDecisionStateMachine { 230 base := h.newDecisionStateMachineBase(decisionTypeActivity, attributes.GetActivityId()) 231 return &activityDecisionStateMachine{ 232 decisionStateMachineBase: base, 233 attributes: attributes, 234 } 235 } 236 237 func (h *decisionsHelper) newTimerDecisionStateMachine(attributes *s.StartTimerDecisionAttributes) *timerDecisionStateMachine { 238 base := h.newDecisionStateMachineBase(decisionTypeTimer, attributes.GetTimerId()) 239 return &timerDecisionStateMachine{ 240 decisionStateMachineBase: base, 241 attributes: attributes, 242 } 243 } 244 245 func (h *decisionsHelper) newChildWorkflowDecisionStateMachine(attributes *s.StartChildWorkflowExecutionDecisionAttributes) *childWorkflowDecisionStateMachine { 246 base := h.newDecisionStateMachineBase(decisionTypeChildWorkflow, attributes.GetWorkflowId()) 247 return &childWorkflowDecisionStateMachine{ 248 decisionStateMachineBase: base, 249 attributes: attributes, 250 } 251 } 252 253 func (h *decisionsHelper) newNaiveDecisionStateMachine(decisionType decisionType, id string, decision *s.Decision) *naiveDecisionStateMachine { 254 base := h.newDecisionStateMachineBase(decisionType, id) 255 return &naiveDecisionStateMachine{ 256 decisionStateMachineBase: base, 257 decision: decision, 258 } 259 } 260 261 func (h *decisionsHelper) newMarkerDecisionStateMachine(id string, attributes *s.RecordMarkerDecisionAttributes) *markerDecisionStateMachine { 262 d := createNewDecision(s.DecisionTypeRecordMarker) 263 d.RecordMarkerDecisionAttributes = attributes 264 return &markerDecisionStateMachine{ 265 naiveDecisionStateMachine: h.newNaiveDecisionStateMachine(decisionTypeMarker, id, d), 266 } 267 } 268 269 func (h *decisionsHelper) newCancelExternalWorkflowStateMachine(attributes *s.RequestCancelExternalWorkflowExecutionDecisionAttributes, cancellationID string) *cancelExternalWorkflowDecisionStateMachine { 270 d := createNewDecision(s.DecisionTypeRequestCancelExternalWorkflowExecution) 271 d.RequestCancelExternalWorkflowExecutionDecisionAttributes = attributes 272 return &cancelExternalWorkflowDecisionStateMachine{ 273 naiveDecisionStateMachine: h.newNaiveDecisionStateMachine(decisionTypeCancellation, cancellationID, d), 274 } 275 } 276 277 func (h *decisionsHelper) newSignalExternalWorkflowStateMachine(attributes *s.SignalExternalWorkflowExecutionDecisionAttributes, signalID string) *signalExternalWorkflowDecisionStateMachine { 278 d := createNewDecision(s.DecisionTypeSignalExternalWorkflowExecution) 279 d.SignalExternalWorkflowExecutionDecisionAttributes = attributes 280 return &signalExternalWorkflowDecisionStateMachine{ 281 naiveDecisionStateMachine: h.newNaiveDecisionStateMachine(decisionTypeSignal, signalID, d), 282 } 283 } 284 285 func (h *decisionsHelper) newUpsertSearchAttributesStateMachine(attributes *s.UpsertWorkflowSearchAttributesDecisionAttributes, upsertID string) *upsertSearchAttributesDecisionStateMachine { 286 d := createNewDecision(s.DecisionTypeUpsertWorkflowSearchAttributes) 287 d.UpsertWorkflowSearchAttributesDecisionAttributes = attributes 288 return &upsertSearchAttributesDecisionStateMachine{ 289 naiveDecisionStateMachine: h.newNaiveDecisionStateMachine(decisionTypeUpsertSearchAttributes, upsertID, d), 290 } 291 } 292 293 func (d *decisionStateMachineBase) getState() decisionState { 294 return d.state 295 } 296 297 func (d *decisionStateMachineBase) getID() decisionID { 298 return d.id 299 } 300 301 func (d *decisionStateMachineBase) isDone() bool { 302 return d.state == decisionStateCompleted || d.state == decisionStateCompletedAfterCancellationDecisionSent 303 } 304 305 func (d *decisionStateMachineBase) setData(data interface{}) { 306 d.data = data 307 } 308 309 func (d *decisionStateMachineBase) getData() interface{} { 310 return d.data 311 } 312 313 func (d *decisionStateMachineBase) moveState(newState decisionState, event string) { 314 d.history = append(d.history, event) 315 d.state = newState 316 d.history = append(d.history, newState.String()) 317 318 if newState == decisionStateCompleted { 319 if elem, ok := d.helper.decisions[d.getID()]; ok { 320 d.helper.orderedDecisions.Remove(elem) 321 delete(d.helper.decisions, d.getID()) 322 } 323 } 324 } 325 326 func (d stateMachineIllegalStatePanic) String() string { 327 return d.message 328 } 329 330 func panicIllegalState(message string) { 331 panic(stateMachineIllegalStatePanic{message: message}) 332 } 333 334 func (d *decisionStateMachineBase) failStateTransition(event string) { 335 // this is when we detect illegal state transition, likely due to ill history sequence or nondeterministic decider code 336 panicIllegalState(fmt.Sprintf("invalid state transition: attempt to %v, %v", event, d)) 337 } 338 339 func (d *decisionStateMachineBase) handleDecisionSent() { 340 switch d.state { 341 case decisionStateCreated: 342 d.moveState(decisionStateDecisionSent, eventDecisionSent) 343 } 344 } 345 346 func (d *decisionStateMachineBase) cancel() { 347 switch d.state { 348 case decisionStateCompleted, decisionStateCompletedAfterCancellationDecisionSent: 349 // No op. This is legit. People could cancel context after timer/activity is done. 350 case decisionStateCreated: 351 d.moveState(decisionStateCompleted, eventCancel) 352 case decisionStateDecisionSent: 353 d.moveState(decisionStateCanceledBeforeInitiated, eventCancel) 354 case decisionStateInitiated: 355 d.moveState(decisionStateCanceledAfterInitiated, eventCancel) 356 default: 357 d.failStateTransition(eventCancel) 358 } 359 } 360 361 func (d *decisionStateMachineBase) handleInitiatedEvent() { 362 switch d.state { 363 case decisionStateDecisionSent: 364 d.moveState(decisionStateInitiated, eventInitiated) 365 case decisionStateCanceledBeforeInitiated: 366 d.moveState(decisionStateCanceledAfterInitiated, eventInitiated) 367 default: 368 d.failStateTransition(eventInitiated) 369 } 370 } 371 372 func (d *decisionStateMachineBase) handleInitiationFailedEvent() { 373 switch d.state { 374 case decisionStateInitiated, decisionStateDecisionSent, decisionStateCanceledBeforeInitiated: 375 d.moveState(decisionStateCompleted, eventInitiationFailed) 376 default: 377 d.failStateTransition(eventInitiationFailed) 378 } 379 } 380 381 func (d *decisionStateMachineBase) handleStartedEvent() { 382 d.history = append(d.history, eventStarted) 383 } 384 385 func (d *decisionStateMachineBase) handleCompletionEvent() { 386 switch d.state { 387 case decisionStateCanceledAfterInitiated, decisionStateInitiated: 388 d.moveState(decisionStateCompleted, eventCompletion) 389 case decisionStateCancellationDecisionSent: 390 d.moveState(decisionStateCompletedAfterCancellationDecisionSent, eventCompletion) 391 default: 392 d.failStateTransition(eventCompletion) 393 } 394 } 395 396 func (d *decisionStateMachineBase) handleCancelInitiatedEvent() { 397 d.history = append(d.history, eventCancelInitiated) 398 switch d.state { 399 case decisionStateCancellationDecisionSent: 400 // No state change 401 default: 402 d.failStateTransition(eventCancelInitiated) 403 } 404 } 405 406 func (d *decisionStateMachineBase) handleCancelFailedEvent() { 407 switch d.state { 408 case decisionStateCompletedAfterCancellationDecisionSent: 409 d.moveState(decisionStateCompleted, eventCancelFailed) 410 default: 411 d.failStateTransition(eventCancelFailed) 412 } 413 } 414 415 func (d *decisionStateMachineBase) handleCanceledEvent() { 416 switch d.state { 417 case decisionStateCancellationDecisionSent: 418 d.moveState(decisionStateCompleted, eventCanceled) 419 default: 420 d.failStateTransition(eventCanceled) 421 } 422 } 423 424 func (d *decisionStateMachineBase) String() string { 425 return fmt.Sprintf("%v, state=%v, isDone()=%v, history=%v", 426 d.id, d.state, d.isDone(), d.history) 427 } 428 429 func (d *activityDecisionStateMachine) getDecision() *s.Decision { 430 switch d.state { 431 case decisionStateCreated: 432 decision := createNewDecision(s.DecisionTypeScheduleActivityTask) 433 decision.ScheduleActivityTaskDecisionAttributes = d.attributes 434 return decision 435 case decisionStateCanceledAfterInitiated: 436 decision := createNewDecision(s.DecisionTypeRequestCancelActivityTask) 437 decision.RequestCancelActivityTaskDecisionAttributes = &s.RequestCancelActivityTaskDecisionAttributes{ 438 ActivityId: d.attributes.ActivityId, 439 } 440 return decision 441 default: 442 return nil 443 } 444 } 445 446 func (d *activityDecisionStateMachine) handleDecisionSent() { 447 switch d.state { 448 case decisionStateCanceledAfterInitiated: 449 d.moveState(decisionStateCancellationDecisionSent, eventDecisionSent) 450 default: 451 d.decisionStateMachineBase.handleDecisionSent() 452 } 453 } 454 455 func (d *activityDecisionStateMachine) handleCancelFailedEvent() { 456 switch d.state { 457 case decisionStateCancellationDecisionSent: 458 d.moveState(decisionStateInitiated, eventCancelFailed) 459 default: 460 d.decisionStateMachineBase.handleCancelFailedEvent() 461 } 462 } 463 464 func (d *timerDecisionStateMachine) cancel() { 465 d.canceled = true 466 d.decisionStateMachineBase.cancel() 467 } 468 469 func (d *timerDecisionStateMachine) isDone() bool { 470 return d.state == decisionStateCompleted || d.canceled 471 } 472 473 func (d *timerDecisionStateMachine) handleDecisionSent() { 474 switch d.state { 475 case decisionStateCanceledAfterInitiated: 476 d.moveState(decisionStateCancellationDecisionSent, eventDecisionSent) 477 default: 478 d.decisionStateMachineBase.handleDecisionSent() 479 } 480 } 481 482 func (d *timerDecisionStateMachine) handleCancelFailedEvent() { 483 switch d.state { 484 case decisionStateCancellationDecisionSent: 485 d.moveState(decisionStateInitiated, eventCancelFailed) 486 default: 487 d.decisionStateMachineBase.handleCancelFailedEvent() 488 } 489 } 490 491 func (d *timerDecisionStateMachine) getDecision() *s.Decision { 492 switch d.state { 493 case decisionStateCreated: 494 decision := createNewDecision(s.DecisionTypeStartTimer) 495 decision.StartTimerDecisionAttributes = d.attributes 496 return decision 497 case decisionStateCanceledAfterInitiated: 498 decision := createNewDecision(s.DecisionTypeCancelTimer) 499 decision.CancelTimerDecisionAttributes = &s.CancelTimerDecisionAttributes{ 500 TimerId: d.attributes.TimerId, 501 } 502 return decision 503 default: 504 return nil 505 } 506 } 507 508 func (d *childWorkflowDecisionStateMachine) getDecision() *s.Decision { 509 switch d.state { 510 case decisionStateCreated: 511 decision := createNewDecision(s.DecisionTypeStartChildWorkflowExecution) 512 decision.StartChildWorkflowExecutionDecisionAttributes = d.attributes 513 return decision 514 case decisionStateCanceledAfterStarted: 515 decision := createNewDecision(s.DecisionTypeRequestCancelExternalWorkflowExecution) 516 decision.RequestCancelExternalWorkflowExecutionDecisionAttributes = &s.RequestCancelExternalWorkflowExecutionDecisionAttributes{ 517 Domain: d.attributes.Domain, 518 WorkflowId: d.attributes.WorkflowId, 519 ChildWorkflowOnly: common.BoolPtr(true), 520 } 521 return decision 522 default: 523 return nil 524 } 525 } 526 527 func (d *childWorkflowDecisionStateMachine) handleDecisionSent() { 528 switch d.state { 529 case decisionStateCanceledAfterStarted: 530 d.moveState(decisionStateCancellationDecisionSent, eventDecisionSent) 531 default: 532 d.decisionStateMachineBase.handleDecisionSent() 533 } 534 } 535 536 func (d *childWorkflowDecisionStateMachine) handleStartedEvent() { 537 switch d.state { 538 case decisionStateInitiated: 539 d.moveState(decisionStateStarted, eventStarted) 540 case decisionStateCanceledAfterInitiated: 541 d.moveState(decisionStateCanceledAfterStarted, eventStarted) 542 default: 543 d.decisionStateMachineBase.handleStartedEvent() 544 } 545 } 546 547 func (d *childWorkflowDecisionStateMachine) handleCancelFailedEvent() { 548 switch d.state { 549 case decisionStateCancellationDecisionSent: 550 d.moveState(decisionStateStarted, eventCancelFailed) 551 default: 552 d.decisionStateMachineBase.handleCancelFailedEvent() 553 } 554 } 555 556 func (d *childWorkflowDecisionStateMachine) cancel() { 557 switch d.state { 558 case decisionStateStarted: 559 d.moveState(decisionStateCanceledAfterStarted, eventCancel) 560 default: 561 d.decisionStateMachineBase.cancel() 562 } 563 } 564 565 func (d *childWorkflowDecisionStateMachine) handleCanceledEvent() { 566 switch d.state { 567 case decisionStateStarted: 568 d.moveState(decisionStateCompleted, eventCanceled) 569 default: 570 d.decisionStateMachineBase.handleCanceledEvent() 571 } 572 } 573 574 func (d *childWorkflowDecisionStateMachine) handleCompletionEvent() { 575 switch d.state { 576 case decisionStateStarted, decisionStateCanceledAfterStarted: 577 d.moveState(decisionStateCompleted, eventCompletion) 578 default: 579 d.decisionStateMachineBase.handleCompletionEvent() 580 } 581 } 582 583 func (d *naiveDecisionStateMachine) getDecision() *s.Decision { 584 switch d.state { 585 case decisionStateCreated: 586 return d.decision 587 default: 588 return nil 589 } 590 } 591 592 func (d *naiveDecisionStateMachine) cancel() { 593 panic("unsupported operation") 594 } 595 596 func (d *naiveDecisionStateMachine) handleCompletionEvent() { 597 panic("unsupported operation") 598 } 599 600 func (d *naiveDecisionStateMachine) handleInitiatedEvent() { 601 panic("unsupported operation") 602 } 603 604 func (d *naiveDecisionStateMachine) handleInitiationFailedEvent() { 605 panic("unsupported operation") 606 } 607 608 func (d *naiveDecisionStateMachine) handleStartedEvent() { 609 panic("unsupported operation") 610 } 611 612 func (d *naiveDecisionStateMachine) handleCanceledEvent() { 613 panic("unsupported operation") 614 } 615 616 func (d *naiveDecisionStateMachine) handleCancelFailedEvent() { 617 panic("unsupported operation") 618 } 619 620 func (d *naiveDecisionStateMachine) handleCancelInitiatedEvent() { 621 panic("unsupported operation") 622 } 623 624 func (d *cancelExternalWorkflowDecisionStateMachine) handleInitiatedEvent() { 625 switch d.state { 626 case decisionStateDecisionSent: 627 d.moveState(decisionStateInitiated, eventInitiated) 628 default: 629 d.failStateTransition(eventInitiated) 630 } 631 } 632 633 func (d *cancelExternalWorkflowDecisionStateMachine) handleCompletionEvent() { 634 switch d.state { 635 case decisionStateInitiated: 636 d.moveState(decisionStateCompleted, eventCompletion) 637 default: 638 d.failStateTransition(eventCompletion) 639 } 640 } 641 642 func (d *signalExternalWorkflowDecisionStateMachine) handleInitiatedEvent() { 643 switch d.state { 644 case decisionStateDecisionSent: 645 d.moveState(decisionStateInitiated, eventInitiated) 646 default: 647 d.failStateTransition(eventInitiated) 648 } 649 } 650 651 func (d *signalExternalWorkflowDecisionStateMachine) handleCompletionEvent() { 652 switch d.state { 653 case decisionStateInitiated: 654 d.moveState(decisionStateCompleted, eventCompletion) 655 default: 656 d.failStateTransition(eventCompletion) 657 } 658 } 659 660 func (d *markerDecisionStateMachine) handleDecisionSent() { 661 // Marker decision state machine is considered as completed once decision is sent. 662 // For SideEffect/Version markers, when the history event is applied, there is no marker decision state machine yet 663 // because we preload those marker events. 664 // For local activity, when we apply the history event, we use it to create the marker state machine, there is no 665 // other event to drive it to completed state. 666 switch d.state { 667 case decisionStateCreated: 668 d.moveState(decisionStateCompleted, eventDecisionSent) 669 } 670 } 671 672 func (d *upsertSearchAttributesDecisionStateMachine) handleDecisionSent() { 673 // This decision is considered as completed once decision is sent. 674 switch d.state { 675 case decisionStateCreated: 676 d.moveState(decisionStateCompleted, eventDecisionSent) 677 } 678 } 679 680 func newDecisionsHelper() *decisionsHelper { 681 return &decisionsHelper{ 682 orderedDecisions: list.New(), 683 decisions: make(map[decisionID]*list.Element), 684 685 scheduledEventIDToActivityID: make(map[int64]string), 686 scheduledEventIDToCancellationID: make(map[int64]string), 687 scheduledEventIDToSignalID: make(map[int64]string), 688 } 689 } 690 691 func (h *decisionsHelper) getDecision(id decisionID) decisionStateMachine { 692 decision, ok := h.decisions[id] 693 if !ok { 694 panicMsg := fmt.Sprintf("unknown decision %v, possible causes are nondeterministic workflow definition code"+ 695 " or incompatible change in the workflow definition", id) 696 panicIllegalState(panicMsg) 697 } 698 // Move the last update decision state machine to the back of the list. 699 // Otherwise decisions (like timer cancellations) can end up out of order. 700 h.orderedDecisions.MoveToBack(decision) 701 return decision.Value.(decisionStateMachine) 702 } 703 704 func (h *decisionsHelper) addDecision(decision decisionStateMachine) { 705 if _, ok := h.decisions[decision.getID()]; ok { 706 panicMsg := fmt.Sprintf("adding duplicate decision %v", decision) 707 panicIllegalState(panicMsg) 708 } 709 element := h.orderedDecisions.PushBack(decision) 710 h.decisions[decision.getID()] = element 711 } 712 713 func (h *decisionsHelper) scheduleActivityTask(attributes *s.ScheduleActivityTaskDecisionAttributes) decisionStateMachine { 714 decision := h.newActivityDecisionStateMachine(attributes) 715 h.addDecision(decision) 716 return decision 717 } 718 719 func (h *decisionsHelper) requestCancelActivityTask(activityID string) decisionStateMachine { 720 id := makeDecisionID(decisionTypeActivity, activityID) 721 decision := h.getDecision(id) 722 decision.cancel() 723 return decision 724 } 725 726 func (h *decisionsHelper) handleActivityTaskClosed(activityID string) decisionStateMachine { 727 decision := h.getDecision(makeDecisionID(decisionTypeActivity, activityID)) 728 decision.handleCompletionEvent() 729 return decision 730 } 731 732 func (h *decisionsHelper) handleActivityTaskScheduled(scheduledEventID int64, activityID string) { 733 h.scheduledEventIDToActivityID[scheduledEventID] = activityID 734 decision := h.getDecision(makeDecisionID(decisionTypeActivity, activityID)) 735 decision.handleInitiatedEvent() 736 } 737 738 func (h *decisionsHelper) handleActivityTaskCancelRequested(activityID string) { 739 decision := h.getDecision(makeDecisionID(decisionTypeActivity, activityID)) 740 decision.handleCancelInitiatedEvent() 741 } 742 743 func (h *decisionsHelper) handleActivityTaskCanceled(activityID string) decisionStateMachine { 744 decision := h.getDecision(makeDecisionID(decisionTypeActivity, activityID)) 745 decision.handleCanceledEvent() 746 return decision 747 } 748 749 func (h *decisionsHelper) handleRequestCancelActivityTaskFailed(activityID string) { 750 decision := h.getDecision(makeDecisionID(decisionTypeActivity, activityID)) 751 decision.handleCancelFailedEvent() 752 } 753 754 func (h *decisionsHelper) getActivityID(event *s.HistoryEvent) string { 755 var scheduledEventID int64 = -1 756 switch event.GetEventType() { 757 case s.EventTypeActivityTaskCanceled: 758 scheduledEventID = event.ActivityTaskCanceledEventAttributes.GetScheduledEventId() 759 case s.EventTypeActivityTaskCompleted: 760 scheduledEventID = event.ActivityTaskCompletedEventAttributes.GetScheduledEventId() 761 case s.EventTypeActivityTaskFailed: 762 scheduledEventID = event.ActivityTaskFailedEventAttributes.GetScheduledEventId() 763 case s.EventTypeActivityTaskTimedOut: 764 scheduledEventID = event.ActivityTaskTimedOutEventAttributes.GetScheduledEventId() 765 default: 766 panicIllegalState(fmt.Sprintf("unexpected event type %v", event.GetEventType())) 767 } 768 769 activityID, ok := h.scheduledEventIDToActivityID[scheduledEventID] 770 if !ok { 771 panicIllegalState(fmt.Sprintf("unable to find activity ID for the event %v", util.HistoryEventToString(event))) 772 } 773 return activityID 774 } 775 776 func (h *decisionsHelper) recordVersionMarker(changeID string, version Version, dataConverter DataConverter) decisionStateMachine { 777 markerID := fmt.Sprintf("%v_%v", versionMarkerName, changeID) 778 details, err := encodeArgs(dataConverter, []interface{}{changeID, version}) 779 if err != nil { 780 panic(err) 781 } 782 783 recordMarker := &s.RecordMarkerDecisionAttributes{ 784 MarkerName: common.StringPtr(versionMarkerName), 785 Details: details, // Keep 786 } 787 788 decision := h.newMarkerDecisionStateMachine(markerID, recordMarker) 789 h.addDecision(decision) 790 return decision 791 } 792 793 func (h *decisionsHelper) recordSideEffectMarker(sideEffectID int32, data []byte) decisionStateMachine { 794 markerID := fmt.Sprintf("%v_%v", sideEffectMarkerName, sideEffectID) 795 attributes := &s.RecordMarkerDecisionAttributes{ 796 MarkerName: common.StringPtr(sideEffectMarkerName), 797 Details: data, 798 } 799 decision := h.newMarkerDecisionStateMachine(markerID, attributes) 800 h.addDecision(decision) 801 return decision 802 } 803 804 func (h *decisionsHelper) recordLocalActivityMarker(activityID string, result []byte) decisionStateMachine { 805 markerID := fmt.Sprintf("%v_%v", localActivityMarkerName, activityID) 806 attributes := &s.RecordMarkerDecisionAttributes{ 807 MarkerName: common.StringPtr(localActivityMarkerName), 808 Details: result, 809 } 810 decision := h.newMarkerDecisionStateMachine(markerID, attributes) 811 h.addDecision(decision) 812 return decision 813 } 814 815 func (h *decisionsHelper) recordMutableSideEffectMarker(mutableSideEffectID string, data []byte) decisionStateMachine { 816 markerID := fmt.Sprintf("%v_%v", mutableSideEffectMarkerName, mutableSideEffectID) 817 attributes := &s.RecordMarkerDecisionAttributes{ 818 MarkerName: common.StringPtr(mutableSideEffectMarkerName), 819 Details: data, 820 } 821 decision := h.newMarkerDecisionStateMachine(markerID, attributes) 822 h.addDecision(decision) 823 return decision 824 } 825 826 func (h *decisionsHelper) startChildWorkflowExecution(attributes *s.StartChildWorkflowExecutionDecisionAttributes) decisionStateMachine { 827 decision := h.newChildWorkflowDecisionStateMachine(attributes) 828 h.addDecision(decision) 829 return decision 830 } 831 832 func (h *decisionsHelper) handleStartChildWorkflowExecutionInitiated(workflowID string) { 833 decision := h.getDecision(makeDecisionID(decisionTypeChildWorkflow, workflowID)) 834 decision.handleInitiatedEvent() 835 } 836 837 func (h *decisionsHelper) handleStartChildWorkflowExecutionFailed(workflowID string) decisionStateMachine { 838 decision := h.getDecision(makeDecisionID(decisionTypeChildWorkflow, workflowID)) 839 decision.handleInitiationFailedEvent() 840 return decision 841 } 842 843 func (h *decisionsHelper) requestCancelExternalWorkflowExecution(domain, workflowID, runID string, cancellationID string, childWorkflowOnly bool) decisionStateMachine { 844 if childWorkflowOnly { 845 // For cancellation of child workflow only, we do not use cancellation ID 846 // since the child workflow cancellation go through the existing child workflow 847 // state machine, and we use workflow ID as identifier 848 // we also do not use run ID, since child workflow can do continue-as-new 849 // which will have different run ID 850 // there will be server side validation that target workflow is child workflow 851 852 // sanity check that cancellation ID is not set 853 if len(cancellationID) != 0 { 854 panic("cancellation on child workflow should not use cancellation ID") 855 } 856 // sanity check that run ID is not set 857 if len(runID) != 0 { 858 panic("cancellation on child workflow should not use run ID") 859 } 860 // targeting child workflow 861 decision := h.getDecision(makeDecisionID(decisionTypeChildWorkflow, workflowID)) 862 decision.cancel() 863 return decision 864 } 865 866 // For cancellation of external workflow, we have to use cancellation ID 867 // to identify different cancellation request (decision) / response (history event) 868 // client can also use this code path to cancel its own child workflow, however, there will 869 // be no server side validation that target workflow is the child 870 871 // sanity check that cancellation ID is set 872 if len(cancellationID) == 0 { 873 panic("cancellation on external workflow should use cancellation ID") 874 } 875 attributes := &s.RequestCancelExternalWorkflowExecutionDecisionAttributes{ 876 Domain: common.StringPtr(domain), 877 WorkflowId: common.StringPtr(workflowID), 878 RunId: common.StringPtr(runID), 879 Control: []byte(cancellationID), 880 ChildWorkflowOnly: common.BoolPtr(false), 881 } 882 decision := h.newCancelExternalWorkflowStateMachine(attributes, cancellationID) 883 h.addDecision(decision) 884 885 return decision 886 } 887 888 func (h *decisionsHelper) handleRequestCancelExternalWorkflowExecutionInitiated(initiatedeventID int64, workflowID, cancellationID string) { 889 if h.isCancelExternalWorkflowEventForChildWorkflow(cancellationID) { 890 // this is cancellation for child workflow only 891 decision := h.getDecision(makeDecisionID(decisionTypeChildWorkflow, workflowID)) 892 decision.handleCancelInitiatedEvent() 893 } else { 894 // this is cancellation for external workflow 895 h.scheduledEventIDToCancellationID[initiatedeventID] = cancellationID 896 decision := h.getDecision(makeDecisionID(decisionTypeCancellation, cancellationID)) 897 decision.handleInitiatedEvent() 898 } 899 } 900 901 func (h *decisionsHelper) handleExternalWorkflowExecutionCancelRequested(initiatedeventID int64, workflowID string) (bool, decisionStateMachine) { 902 var decision decisionStateMachine 903 cancellationID, isExternal := h.scheduledEventIDToCancellationID[initiatedeventID] 904 if !isExternal { 905 decision = h.getDecision(makeDecisionID(decisionTypeChildWorkflow, workflowID)) 906 // no state change for child workflow, it is still in CancellationDecisionSent 907 } else { 908 // this is cancellation for external workflow 909 decision = h.getDecision(makeDecisionID(decisionTypeCancellation, cancellationID)) 910 decision.handleCompletionEvent() 911 } 912 return isExternal, decision 913 } 914 915 func (h *decisionsHelper) handleRequestCancelExternalWorkflowExecutionFailed(initiatedeventID int64, workflowID string) (bool, decisionStateMachine) { 916 var decision decisionStateMachine 917 cancellationID, isExternal := h.scheduledEventIDToCancellationID[initiatedeventID] 918 if !isExternal { 919 // this is cancellation for child workflow only 920 decision = h.getDecision(makeDecisionID(decisionTypeChildWorkflow, workflowID)) 921 decision.handleCancelFailedEvent() 922 } else { 923 // this is cancellation for external workflow 924 decision = h.getDecision(makeDecisionID(decisionTypeCancellation, cancellationID)) 925 decision.handleCompletionEvent() 926 } 927 return isExternal, decision 928 } 929 930 func (h *decisionsHelper) signalExternalWorkflowExecution(domain, workflowID, runID, signalName string, input []byte, signalID string, childWorkflowOnly bool) decisionStateMachine { 931 attributes := &s.SignalExternalWorkflowExecutionDecisionAttributes{ 932 Domain: common.StringPtr(domain), 933 Execution: &s.WorkflowExecution{ 934 WorkflowId: common.StringPtr(workflowID), 935 RunId: common.StringPtr(runID), 936 }, 937 SignalName: common.StringPtr(signalName), 938 Input: input, 939 Control: []byte(signalID), 940 ChildWorkflowOnly: common.BoolPtr(childWorkflowOnly), 941 } 942 decision := h.newSignalExternalWorkflowStateMachine(attributes, signalID) 943 h.addDecision(decision) 944 return decision 945 } 946 947 func (h *decisionsHelper) upsertSearchAttributes(upsertID string, searchAttr *s.SearchAttributes) decisionStateMachine { 948 attributes := &s.UpsertWorkflowSearchAttributesDecisionAttributes{ 949 SearchAttributes: searchAttr, 950 } 951 decision := h.newUpsertSearchAttributesStateMachine(attributes, upsertID) 952 h.addDecision(decision) 953 return decision 954 } 955 956 func (h *decisionsHelper) handleSignalExternalWorkflowExecutionInitiated(initiatedEventID int64, signalID string) { 957 h.scheduledEventIDToSignalID[initiatedEventID] = signalID 958 decision := h.getDecision(makeDecisionID(decisionTypeSignal, signalID)) 959 decision.handleInitiatedEvent() 960 } 961 962 func (h *decisionsHelper) handleSignalExternalWorkflowExecutionCompleted(initiatedEventID int64) decisionStateMachine { 963 decision := h.getDecision(makeDecisionID(decisionTypeSignal, h.getSignalID(initiatedEventID))) 964 decision.handleCompletionEvent() 965 return decision 966 } 967 968 func (h *decisionsHelper) handleSignalExternalWorkflowExecutionFailed(initiatedEventID int64) decisionStateMachine { 969 decision := h.getDecision(makeDecisionID(decisionTypeSignal, h.getSignalID(initiatedEventID))) 970 decision.handleCompletionEvent() 971 return decision 972 } 973 974 func (h *decisionsHelper) getSignalID(initiatedEventID int64) string { 975 signalID, ok := h.scheduledEventIDToSignalID[initiatedEventID] 976 if !ok { 977 panic(fmt.Sprintf("unable to find signal ID: %v", initiatedEventID)) 978 } 979 return signalID 980 } 981 982 func (h *decisionsHelper) startTimer(attributes *s.StartTimerDecisionAttributes) decisionStateMachine { 983 decision := h.newTimerDecisionStateMachine(attributes) 984 h.addDecision(decision) 985 return decision 986 } 987 988 func (h *decisionsHelper) cancelTimer(timerID string) decisionStateMachine { 989 decision := h.getDecision(makeDecisionID(decisionTypeTimer, timerID)) 990 decision.cancel() 991 return decision 992 } 993 994 func (h *decisionsHelper) handleTimerClosed(timerID string) decisionStateMachine { 995 decision := h.getDecision(makeDecisionID(decisionTypeTimer, timerID)) 996 decision.handleCompletionEvent() 997 return decision 998 } 999 1000 func (h *decisionsHelper) handleTimerStarted(timerID string) { 1001 decision := h.getDecision(makeDecisionID(decisionTypeTimer, timerID)) 1002 decision.handleInitiatedEvent() 1003 } 1004 1005 func (h *decisionsHelper) handleTimerCanceled(timerID string) { 1006 decision := h.getDecision(makeDecisionID(decisionTypeTimer, timerID)) 1007 decision.handleCanceledEvent() 1008 } 1009 1010 func (h *decisionsHelper) handleCancelTimerFailed(timerID string) { 1011 decision := h.getDecision(makeDecisionID(decisionTypeTimer, timerID)) 1012 decision.handleCancelFailedEvent() 1013 } 1014 1015 func (h *decisionsHelper) handleChildWorkflowExecutionStarted(workflowID string) decisionStateMachine { 1016 decision := h.getDecision(makeDecisionID(decisionTypeChildWorkflow, workflowID)) 1017 decision.handleStartedEvent() 1018 return decision 1019 } 1020 1021 func (h *decisionsHelper) handleChildWorkflowExecutionClosed(workflowID string) decisionStateMachine { 1022 decision := h.getDecision(makeDecisionID(decisionTypeChildWorkflow, workflowID)) 1023 decision.handleCompletionEvent() 1024 return decision 1025 } 1026 1027 func (h *decisionsHelper) handleChildWorkflowExecutionCanceled(workflowID string) decisionStateMachine { 1028 decision := h.getDecision(makeDecisionID(decisionTypeChildWorkflow, workflowID)) 1029 decision.handleCanceledEvent() 1030 return decision 1031 } 1032 1033 func (h *decisionsHelper) getDecisions(markAsSent bool) []*s.Decision { 1034 var result []*s.Decision 1035 for curr := h.orderedDecisions.Front(); curr != nil; { 1036 next := curr.Next() // get next item here as we might need to remove curr in the loop 1037 d := curr.Value.(decisionStateMachine) 1038 decision := d.getDecision() 1039 if decision != nil { 1040 result = append(result, decision) 1041 } 1042 1043 if markAsSent { 1044 d.handleDecisionSent() 1045 } 1046 1047 // remove completed decision state machines 1048 if d.getState() == decisionStateCompleted { 1049 h.orderedDecisions.Remove(curr) 1050 delete(h.decisions, d.getID()) 1051 } 1052 1053 curr = next 1054 } 1055 1056 return result 1057 } 1058 1059 func (h *decisionsHelper) isCancelExternalWorkflowEventForChildWorkflow(cancellationID string) bool { 1060 // the cancellationID, i.e. Control in RequestCancelExternalWorkflowExecutionInitiatedEventAttributes 1061 // will be empty if the event is for child workflow. 1062 // for cancellation external workflow, Control in RequestCancelExternalWorkflowExecutionInitiatedEventAttributes 1063 // will have a client generated sequence ID 1064 return len(cancellationID) == 0 1065 }