@@ -265,65 +265,87 @@ func GetSignalChannel(ctx Context, signalName string) Channel {
265265 return internal .GetSignalChannel (ctx , signalName )
266266}
267267
268- // SideEffect executes the provided function once, records its result into the workflow history. The recorded result on
269- // history will be returned without executing the provided function during replay. This guarantees the deterministic
270- // requirement for workflow as the exact same result will be returned in replay.
271- // Common use case is to run some short non-deterministic code in workflow, like getting random number or new UUID.
272- // The only way to fail SideEffect is to panic which causes decision task failure. The decision task after timeout is
273- // rescheduled and re-executed giving SideEffect another chance to succeed.
274- //
275- // Caution: do not use SideEffect to modify closures. Always retrieve result from SideEffect's encoded return value.
276- // For example this code is BROKEN:
268+ // SideEffect executes the provided callback, and records its result into the workflow history.
269+ // During replay the recorded result will be returned instead, so the callback can do non-deterministic
270+ // things (such as reading files or getting random numbers) without breaking determinism during replay.
271+ //
272+ // As there is no error return, the callback's code is assumed to be reliable.
273+ // If you cannot retrieve the value for some reason, panicking and failing the decision task
274+ // will cause it to be retried, possibly succeeding on another machine.
275+ // For better error handling, use ExecuteLocalActivity instead.
276+ //
277+ // Caution: the callback MUST NOT call any blocking or history-creating APIs,
278+ // e.g. workflow.Sleep or ExecuteActivity or calling .Get on any future.
279+ // This will (potentially) work during the initial recording of history, but will
280+ // fail when replaying because the data is not available when the call occurs
281+ // (it becomes available later).
282+ //
283+ // In other words, in general: use this *only* for code that does not need the workflow.Context.
284+ // Incorrect context-using calls are not currently prevented in tests or during execution,
285+ // but they should be eventually.
286+ //
287+ // // Bad example: this will work until a replay occurs,
288+ // // but then the workflow will fail to replay with a non-deterministic error.
289+ // var out string
290+ // err := workflow.SideEffect(func(ctx workflow.Context) interface{} {
291+ // var signal data
292+ // err := workflow.GetSignalChannel(ctx, "signal").Receive(ctx, &signal)
293+ // _ = err // ignore err for the example
294+ // return signal
295+ // }).Get(&out)
296+ // workflow.GetLogger(ctx).Info(out)
297+ //
298+ // Caution: do not use SideEffect to modify values outside the callback.
299+ // Always retrieve result from SideEffect's encoded return value.
300+ // For example this code will break during replay:
277301//
278302// // Bad example:
279303// var random int
280304// workflow.SideEffect(func(ctx workflow.Context) interface{} {
281- // random = rand.Intn(100)
282- // return nil
305+ // random = rand.Intn(100) // this only occurs when recording history, not during replay
306+ // return nil
283307// })
284308// // random will always be 0 in replay, thus this code is non-deterministic
285309// if random < 50 {
286- // ....
310+ // ....
287311// } else {
288- // ....
312+ // ....
289313// }
290314//
291- // On replay the provided function is not executed, the random will always be 0, and the workflow could takes a
292- // different path breaking the determinism.
315+ // On replay the provided function is not executed, the ` random` var will always be 0,
316+ // and the workflow could take a different path and break determinism.
293317//
294- // Here is the correct way to use SideEffect:
318+ // The only safe way to use SideEffect is to read from the returned encoded.Value :
295319//
296320// // Good example:
297321// encodedRandom := SideEffect(func(ctx workflow.Context) interface{} {
298- // return rand.Intn(100)
322+ // return rand.Intn(100)
299323// })
300324// var random int
301325// encodedRandom.Get(&random)
302326// if random < 50 {
303- // ....
327+ // ....
304328// } else {
305- // ....
329+ // ....
306330// }
307331func SideEffect (ctx Context , f func (ctx Context ) interface {}) encoded.Value {
308332 return internal .SideEffect (ctx , f )
309333}
310334
311- // MutableSideEffect executes the provided function once, then it looks up the history for the value with the given id.
312- // If there is no existing value, then it records the function result as a value with the given id on history;
313- // otherwise, it compares whether the existing value from history has changed from the new function result by calling the
314- // provided equals function. If they are equal, it returns the value without recording a new one in history;
315- //
316- // otherwise, it records the new value with the same id on history.
335+ // MutableSideEffect is similar to SideEffect, but it only records changed values per ID instead of every call.
336+ // Changes are detected by calling the provided "equals" func - return true to mark a result
337+ // as the same as before, and not record the new value. The first call per ID is always "changed" and will always be recorded.
317338//
318- // Caution: do not use MutableSideEffect to modify closures. Always retrieve result from MutableSideEffect's encoded
319- // return value.
339+ // This is intended for things you want to *check* frequently, but which *change* only occasionally, as non-changing
340+ // results will not add anything to your history. This helps keep those workflows under its size/count limits, and
341+ // can help keep replays more efficient than when using SideEffect or ExecuteLocalActivity (due to not storing duplicated data).
320342//
321- // The difference between MutableSideEffect() and SideEffect() is that every new SideEffect() call in non-replay will
322- // result in a new marker being recorded on history. However, MutableSideEffect() only records a new marker if the value
323- // changed. During replay, MutableSideEffect() will not execute the function again, but it will return the exact same
324- // value as it was returning during the non-replay run.
343+ // Caution: the callback MUST NOT block or call any history-modifying events, or replays will be broken.
344+ // See SideEffect docs for more details.
325345//
326- // One good use case of MutableSideEffect() is to access dynamically changing config without breaking determinism.
346+ // Caution: do not use MutableSideEffect to modify closures.
347+ // Always retrieve result from MutableSideEffect's encoded return value.
348+ // See SideEffect docs for more details.
327349func MutableSideEffect (ctx Context , id string , f func (ctx Context ) interface {}, equals func (a , b interface {}) bool ) encoded.Value {
328350 return internal .MutableSideEffect (ctx , id , f , equals )
329351}
0 commit comments