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

     1  // Copyright (c) 2017-2021 Uber Technologies Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package internal
    22  
    23  import (
    24  	"bytes"
    25  	"reflect"
    26  	"strings"
    27  
    28  	s "go.uber.org/cadence/.gen/go/shared"
    29  )
    30  
    31  func matchReplayWithHistory(info *WorkflowInfo, replayDecisions []*s.Decision, historyEvents []*s.HistoryEvent) error {
    32  	di := 0
    33  	hi := 0
    34  	hSize := len(historyEvents)
    35  	dSize := len(replayDecisions)
    36  matchLoop:
    37  	for hi < hSize || di < dSize {
    38  		var e *s.HistoryEvent
    39  		if hi < hSize {
    40  			e = historyEvents[hi]
    41  			if skipDeterministicCheckForUpsertChangeVersion(historyEvents, hi) {
    42  				hi += 2
    43  				continue matchLoop
    44  			}
    45  			if skipDeterministicCheckForEvent(e) {
    46  				hi++
    47  				continue matchLoop
    48  			}
    49  		}
    50  
    51  		var d *s.Decision
    52  		if di < dSize {
    53  			d = replayDecisions[di]
    54  			if skipDeterministicCheckForDecision(d) {
    55  				di++
    56  				continue matchLoop
    57  			}
    58  		}
    59  
    60  		if d == nil {
    61  			return NewNonDeterminsticError("missing replay decision", info, e, nil)
    62  		}
    63  
    64  		if e == nil {
    65  			return NewNonDeterminsticError("extra replay decision", info, nil, d)
    66  		}
    67  
    68  		if !isDecisionMatchEvent(d, e, false) {
    69  			return NewNonDeterminsticError("mismatch", info, e, d)
    70  		}
    71  
    72  		di++
    73  		hi++
    74  	}
    75  	return nil
    76  }
    77  
    78  func lastPartOfName(name string) string {
    79  	name = strings.TrimSuffix(name, "-fm")
    80  	lastDotIdx := strings.LastIndex(name, ".")
    81  	if lastDotIdx < 0 || lastDotIdx == len(name)-1 {
    82  		return name
    83  	}
    84  	return name[lastDotIdx+1:]
    85  }
    86  
    87  func skipDeterministicCheckForEvent(e *s.HistoryEvent) bool {
    88  	if e.GetEventType() == s.EventTypeMarkerRecorded {
    89  		markerName := e.MarkerRecordedEventAttributes.GetMarkerName()
    90  		if markerName == versionMarkerName || markerName == mutableSideEffectMarkerName {
    91  			return true
    92  		}
    93  	}
    94  	return false
    95  }
    96  
    97  // special check for upsert change version event
    98  func skipDeterministicCheckForUpsertChangeVersion(events []*s.HistoryEvent, idx int) bool {
    99  	e := events[idx]
   100  	if e.GetEventType() == s.EventTypeMarkerRecorded &&
   101  		e.MarkerRecordedEventAttributes.GetMarkerName() == versionMarkerName &&
   102  		idx < len(events)-1 &&
   103  		events[idx+1].GetEventType() == s.EventTypeUpsertWorkflowSearchAttributes {
   104  		if _, ok := events[idx+1].UpsertWorkflowSearchAttributesEventAttributes.SearchAttributes.IndexedFields[CadenceChangeVersion]; ok {
   105  			return true
   106  		}
   107  	}
   108  	return false
   109  }
   110  
   111  func skipDeterministicCheckForDecision(d *s.Decision) bool {
   112  	if d.GetDecisionType() == s.DecisionTypeRecordMarker {
   113  		markerName := d.RecordMarkerDecisionAttributes.GetMarkerName()
   114  		if markerName == versionMarkerName || markerName == mutableSideEffectMarkerName {
   115  			return true
   116  		}
   117  	}
   118  	return false
   119  }
   120  
   121  func isDecisionMatchEvent(d *s.Decision, e *s.HistoryEvent, strictMode bool) bool {
   122  	switch d.GetDecisionType() {
   123  	case s.DecisionTypeScheduleActivityTask:
   124  		if e.GetEventType() != s.EventTypeActivityTaskScheduled {
   125  			return false
   126  		}
   127  		eventAttributes := e.ActivityTaskScheduledEventAttributes
   128  		decisionAttributes := d.ScheduleActivityTaskDecisionAttributes
   129  
   130  		if eventAttributes.GetActivityId() != decisionAttributes.GetActivityId() ||
   131  			lastPartOfName(eventAttributes.ActivityType.GetName()) != lastPartOfName(decisionAttributes.ActivityType.GetName()) ||
   132  			(strictMode && eventAttributes.TaskList.GetName() != decisionAttributes.TaskList.GetName()) ||
   133  			(strictMode && bytes.Compare(eventAttributes.Input, decisionAttributes.Input) != 0) {
   134  			return false
   135  		}
   136  
   137  		return true
   138  
   139  	case s.DecisionTypeRequestCancelActivityTask:
   140  		if e.GetEventType() != s.EventTypeActivityTaskCancelRequested {
   141  			return false
   142  		}
   143  		decisionAttributes := d.RequestCancelActivityTaskDecisionAttributes
   144  		eventAttributes := e.ActivityTaskCancelRequestedEventAttributes
   145  		if eventAttributes.GetActivityId() != decisionAttributes.GetActivityId() {
   146  			return false
   147  		}
   148  
   149  		return true
   150  
   151  	case s.DecisionTypeStartTimer:
   152  		if e.GetEventType() != s.EventTypeTimerStarted {
   153  			return false
   154  		}
   155  		eventAttributes := e.TimerStartedEventAttributes
   156  		decisionAttributes := d.StartTimerDecisionAttributes
   157  
   158  		if eventAttributes.GetTimerId() != decisionAttributes.GetTimerId() ||
   159  			(strictMode && eventAttributes.GetStartToFireTimeoutSeconds() != decisionAttributes.GetStartToFireTimeoutSeconds()) {
   160  			return false
   161  		}
   162  
   163  		return true
   164  
   165  	case s.DecisionTypeCancelTimer:
   166  		if e.GetEventType() != s.EventTypeTimerCanceled && e.GetEventType() != s.EventTypeCancelTimerFailed {
   167  			return false
   168  		}
   169  		decisionAttributes := d.CancelTimerDecisionAttributes
   170  		if e.GetEventType() == s.EventTypeTimerCanceled {
   171  			eventAttributes := e.TimerCanceledEventAttributes
   172  			if eventAttributes.GetTimerId() != decisionAttributes.GetTimerId() {
   173  				return false
   174  			}
   175  		} else if e.GetEventType() == s.EventTypeCancelTimerFailed {
   176  			eventAttributes := e.CancelTimerFailedEventAttributes
   177  			if eventAttributes.GetTimerId() != decisionAttributes.GetTimerId() {
   178  				return false
   179  			}
   180  		}
   181  
   182  		return true
   183  
   184  	case s.DecisionTypeCompleteWorkflowExecution:
   185  		if e.GetEventType() != s.EventTypeWorkflowExecutionCompleted {
   186  			return false
   187  		}
   188  		if strictMode {
   189  			eventAttributes := e.WorkflowExecutionCompletedEventAttributes
   190  			decisionAttributes := d.CompleteWorkflowExecutionDecisionAttributes
   191  
   192  			if bytes.Compare(eventAttributes.Result, decisionAttributes.Result) != 0 {
   193  				return false
   194  			}
   195  		}
   196  
   197  		return true
   198  
   199  	case s.DecisionTypeFailWorkflowExecution:
   200  		if e.GetEventType() != s.EventTypeWorkflowExecutionFailed {
   201  			return false
   202  		}
   203  		if strictMode {
   204  			eventAttributes := e.WorkflowExecutionFailedEventAttributes
   205  			decisionAttributes := d.FailWorkflowExecutionDecisionAttributes
   206  
   207  			if eventAttributes.GetReason() != decisionAttributes.GetReason() ||
   208  				bytes.Compare(eventAttributes.Details, decisionAttributes.Details) != 0 {
   209  				return false
   210  			}
   211  		}
   212  
   213  		return true
   214  
   215  	case s.DecisionTypeRecordMarker:
   216  		if e.GetEventType() != s.EventTypeMarkerRecorded {
   217  			return false
   218  		}
   219  		eventAttributes := e.MarkerRecordedEventAttributes
   220  		decisionAttributes := d.RecordMarkerDecisionAttributes
   221  		if eventAttributes.GetMarkerName() != decisionAttributes.GetMarkerName() {
   222  			return false
   223  		}
   224  
   225  		return true
   226  
   227  	case s.DecisionTypeRequestCancelExternalWorkflowExecution:
   228  		if e.GetEventType() != s.EventTypeRequestCancelExternalWorkflowExecutionInitiated {
   229  			return false
   230  		}
   231  		eventAttributes := e.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes
   232  		decisionAttributes := d.RequestCancelExternalWorkflowExecutionDecisionAttributes
   233  		if checkDomainsInDecisionAndEvent(eventAttributes.GetDomain(), decisionAttributes.GetDomain()) ||
   234  			eventAttributes.WorkflowExecution.GetWorkflowId() != decisionAttributes.GetWorkflowId() {
   235  			return false
   236  		}
   237  
   238  		return true
   239  
   240  	case s.DecisionTypeSignalExternalWorkflowExecution:
   241  		if e.GetEventType() != s.EventTypeSignalExternalWorkflowExecutionInitiated {
   242  			return false
   243  		}
   244  		eventAttributes := e.SignalExternalWorkflowExecutionInitiatedEventAttributes
   245  		decisionAttributes := d.SignalExternalWorkflowExecutionDecisionAttributes
   246  		if checkDomainsInDecisionAndEvent(eventAttributes.GetDomain(), decisionAttributes.GetDomain()) ||
   247  			eventAttributes.GetSignalName() != decisionAttributes.GetSignalName() ||
   248  			eventAttributes.WorkflowExecution.GetWorkflowId() != decisionAttributes.Execution.GetWorkflowId() {
   249  			return false
   250  		}
   251  
   252  		return true
   253  
   254  	case s.DecisionTypeCancelWorkflowExecution:
   255  		if e.GetEventType() != s.EventTypeWorkflowExecutionCanceled {
   256  			return false
   257  		}
   258  		if strictMode {
   259  			eventAttributes := e.WorkflowExecutionCanceledEventAttributes
   260  			decisionAttributes := d.CancelWorkflowExecutionDecisionAttributes
   261  			if bytes.Compare(eventAttributes.Details, decisionAttributes.Details) != 0 {
   262  				return false
   263  			}
   264  		}
   265  		return true
   266  
   267  	case s.DecisionTypeContinueAsNewWorkflowExecution:
   268  		if e.GetEventType() != s.EventTypeWorkflowExecutionContinuedAsNew {
   269  			return false
   270  		}
   271  
   272  		return true
   273  
   274  	case s.DecisionTypeStartChildWorkflowExecution:
   275  		if e.GetEventType() != s.EventTypeStartChildWorkflowExecutionInitiated {
   276  			return false
   277  		}
   278  		eventAttributes := e.StartChildWorkflowExecutionInitiatedEventAttributes
   279  		decisionAttributes := d.StartChildWorkflowExecutionDecisionAttributes
   280  		if lastPartOfName(eventAttributes.WorkflowType.GetName()) != lastPartOfName(decisionAttributes.WorkflowType.GetName()) ||
   281  			(strictMode && checkDomainsInDecisionAndEvent(eventAttributes.GetDomain(), decisionAttributes.GetDomain())) ||
   282  			(strictMode && eventAttributes.TaskList.GetName() != decisionAttributes.TaskList.GetName()) {
   283  			return false
   284  		}
   285  
   286  		return true
   287  
   288  	case s.DecisionTypeUpsertWorkflowSearchAttributes:
   289  		if e.GetEventType() != s.EventTypeUpsertWorkflowSearchAttributes {
   290  			return false
   291  		}
   292  		eventAttributes := e.UpsertWorkflowSearchAttributesEventAttributes
   293  		decisionAttributes := d.UpsertWorkflowSearchAttributesDecisionAttributes
   294  		if strictMode && !isSearchAttributesMatched(eventAttributes.SearchAttributes, decisionAttributes.SearchAttributes) {
   295  			return false
   296  		}
   297  		return true
   298  	}
   299  
   300  	return false
   301  }
   302  
   303  func isSearchAttributesMatched(attrFromEvent, attrFromDecision *s.SearchAttributes) bool {
   304  	if attrFromEvent != nil && attrFromDecision != nil {
   305  		return reflect.DeepEqual(attrFromEvent.IndexedFields, attrFromDecision.IndexedFields)
   306  	}
   307  	return attrFromEvent == nil && attrFromDecision == nil
   308  }
   309  
   310  // return true if the check fails:
   311  //
   312  //	domain is not empty in decision
   313  //	and domain is not replayDomain
   314  //	and domains unmatch in decision and events
   315  func checkDomainsInDecisionAndEvent(eventDomainName, decisionDomainName string) bool {
   316  	if decisionDomainName == "" || IsReplayDomain(decisionDomainName) {
   317  		return false
   318  	}
   319  	return eventDomainName != decisionDomainName
   320  }