@@ -123,6 +123,16 @@ func Invoke(
123123 return ret , nil
124124}
125125
126+ // UpdateSideEffectOptions is used to track which WorkflowOptions have side effects,
127+ // and cannot be detected simply by proto.Equal.
128+ type UpdateSideEffectOptions struct {
129+ timeSkippingConfig bool
130+ }
131+
132+ func (u UpdateSideEffectOptions ) hasChanges () bool {
133+ return u .timeSkippingConfig
134+ }
135+
126136// MergeAndApply merges the requested options mentioned in the field mask with the current options in the mutable state
127137// and applies the changes to the mutable state. Returns the merged options and a boolean indicating if there were any changes.
128138func MergeAndApply (
@@ -132,7 +142,7 @@ func MergeAndApply(
132142 identity string ,
133143) (* workflowpb.WorkflowExecutionOptions , bool , error ) {
134144 // Merge the requested options mentioned in the field mask with the current options in the mutable state
135- mergedOpts , err := mergeWorkflowExecutionOptions (
145+ mergedOpts , sideEffects , err := mergeWorkflowExecutionOptions (
136146 getOptionsFromMutableState (ms ),
137147 opts ,
138148 updateMask ,
@@ -141,17 +151,15 @@ func MergeAndApply(
141151 return nil , false , serviceerror .NewInvalidArgumentf ("error applying update_options: %v" , err )
142152 }
143153
144- // If there is no mutable state change at all, return with no new history event and Noop=true
145- hasChanges := ! proto .Equal (mergedOpts , getOptionsFromMutableState (ms ))
154+ // sideEffects.hasChanges() forces hasChanges=true for bound renewals where proto.Equal sees no difference.
155+ hasChanges := ! proto .Equal (mergedOpts , getOptionsFromMutableState (ms )) || sideEffects . hasChanges ()
146156 if ! hasChanges {
147157 return mergedOpts , false , nil
148158 }
149159
150- unsetOverride := false
151- if mergedOpts .GetVersioningOverride () == nil {
152- unsetOverride = true
153- }
154- _ , err = ms .AddWorkflowExecutionOptionsUpdatedEvent (mergedOpts .GetVersioningOverride (), unsetOverride , "" , nil , nil , identity , mergedOpts .GetPriority (), mergedOpts .GetTimeSkippingConfig (), nil )
160+ unsetOverride := mergedOpts .GetVersioningOverride () == nil
161+ _ , err = ms .AddWorkflowExecutionOptionsUpdatedEvent (
162+ mergedOpts .GetVersioningOverride (), unsetOverride , "" , nil , nil , identity , mergedOpts .GetPriority (), mergedOpts .GetTimeSkippingConfig (), nil )
155163 if err != nil {
156164 return nil , hasChanges , err
157165 }
@@ -180,30 +188,32 @@ func getOptionsFromMutableState(ms historyi.MutableState) *workflowpb.WorkflowEx
180188 return opts
181189}
182190
183- // mergeWorkflowExecutionOptions copies the given paths in `src` struct to `dst` struct
191+ // mergeWorkflowExecutionOptions copies the given paths in `src` struct to `dst` struct and returns
192+ // the side-effect options for fields that produce side effects when applied (see UpdateSideEffectOptions).
184193func mergeWorkflowExecutionOptions (
185194 mergeInto , mergeFrom * workflowpb.WorkflowExecutionOptions ,
186195 updateMask * fieldmaskpb.FieldMask ,
187- ) (* workflowpb.WorkflowExecutionOptions , error ) {
196+ ) (* workflowpb.WorkflowExecutionOptions , UpdateSideEffectOptions , error ) {
188197 _ , err := fieldmaskpb .New (mergeInto , updateMask .GetPaths ()... )
189198 if err != nil { // errors if any paths are not valid for the struct we are merging into
190- return nil , err
199+ return nil , UpdateSideEffectOptions {}, err
191200 }
192201 updateFields := util .ParseFieldMask (updateMask )
202+
193203 if _ , ok := updateFields ["versioningOverride" ]; ok {
194204 mergeInto .VersioningOverride = mergeFrom .GetVersioningOverride ()
195205 }
196206
197207 if _ , ok := updateFields ["versioningOverride.deployment" ]; ok {
198208 if _ , ok := updateFields ["versioningOverride.behavior" ]; ! ok {
199- return nil , serviceerror .NewInvalidArgument ("versioning_override fields must be updated together" )
209+ return nil , UpdateSideEffectOptions {}, serviceerror .NewInvalidArgument ("versioning_override fields must be updated together" )
200210 }
201211 mergeInto .VersioningOverride = mergeFrom .GetVersioningOverride ()
202212 }
203213
204214 if _ , ok := updateFields ["versioningOverride.behavior" ]; ok {
205215 if _ , ok := updateFields ["versioningOverride.deployment" ]; ! ok {
206- return nil , serviceerror .NewInvalidArgument ("versioning_override fields must be updated together" )
216+ return nil , UpdateSideEffectOptions {}, serviceerror .NewInvalidArgument ("versioning_override fields must be updated together" )
207217 }
208218 mergeInto .VersioningOverride = mergeFrom .GetVersioningOverride ()
209219 }
@@ -235,12 +245,23 @@ func mergeWorkflowExecutionOptions(
235245 mergeInto .Priority .FairnessWeight = mergeFrom .Priority .GetFairnessWeight ()
236246 }
237247
238- // ==== Time Skipping Config
239- // nil means "no change" — only update if the caller provided an explicit value.
248+ // ==== Time Skipping Config (has side effects when applied)
249+ var originalTsc * workflowpb.TimeSkippingConfig
250+ if tsc := mergeInto .GetTimeSkippingConfig (); tsc != nil {
251+ if cloned , ok := proto .Clone (tsc ).(* workflowpb.TimeSkippingConfig ); ok {
252+ originalTsc = cloned
253+ }
254+ }
255+
256+ // getting the new TSC config
257+ var boundInMask bool
240258 if _ , ok := updateFields ["timeSkippingConfig" ]; ok {
241259 if mergeFrom .GetTimeSkippingConfig () != nil {
242260 mergeInto .TimeSkippingConfig = mergeFrom .GetTimeSkippingConfig ()
243261 }
262+ if mergeFrom .GetTimeSkippingConfig ().GetBound () != nil {
263+ boundInMask = true
264+ }
244265 }
245266
246267 if _ , ok := updateFields ["timeSkippingConfig.enabled" ]; ok {
@@ -257,7 +278,16 @@ func mergeWorkflowExecutionOptions(
257278 mergeInto .TimeSkippingConfig .Bound = & workflowpb.TimeSkippingConfig_MaxElapsedDuration {
258279 MaxElapsedDuration : mergeFrom .GetTimeSkippingConfig ().GetMaxElapsedDuration (),
259280 }
281+ boundInMask = true
260282 }
261283
262- return mergeInto , nil
284+ var sideEffects UpdateSideEffectOptions
285+ // either bound is used or the config contents are changed
286+ if boundInMask || (! proto .Equal (mergeInto .GetTimeSkippingConfig (), originalTsc )) {
287+ sideEffects .timeSkippingConfig = true
288+ } else {
289+ sideEffects .timeSkippingConfig = false
290+ mergeInto .TimeSkippingConfig = nil
291+ }
292+ return mergeInto , sideEffects , nil
263293}
0 commit comments