mirror of
https://github.com/rocky-linux/peridot.git
synced 2024-12-26 04:00:55 +00:00
490 lines
25 KiB
Go
490 lines
25 KiB
Go
|
// The MIT License
|
||
|
//
|
||
|
// Copyright (c) 2020 Temporal Technologies Inc. All rights reserved.
|
||
|
//
|
||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||
|
//
|
||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
// of this software and associated documentation files (the "Software"), to deal
|
||
|
// in the Software without restriction, including without limitation the rights
|
||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
// copies of the Software, and to permit persons to whom the Software is
|
||
|
// furnished to do so, subject to the following conditions:
|
||
|
//
|
||
|
// The above copyright notice and this permission notice shall be included in
|
||
|
// all copies or substantial portions of the Software.
|
||
|
//
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
|
// THE SOFTWARE.
|
||
|
|
||
|
package workflow
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
|
||
|
"go.temporal.io/sdk/converter"
|
||
|
"go.temporal.io/sdk/internal"
|
||
|
"go.temporal.io/sdk/internal/common/metrics"
|
||
|
"go.temporal.io/sdk/log"
|
||
|
)
|
||
|
|
||
|
type (
|
||
|
|
||
|
// ChildWorkflowFuture represents the result of a child workflow execution
|
||
|
ChildWorkflowFuture = internal.ChildWorkflowFuture
|
||
|
|
||
|
// Type identifies a workflow type.
|
||
|
Type = internal.WorkflowType
|
||
|
|
||
|
// Execution Details.
|
||
|
Execution = internal.WorkflowExecution
|
||
|
|
||
|
// Version represents a change version. See GetVersion call.
|
||
|
Version = internal.Version
|
||
|
|
||
|
// ChildWorkflowOptions stores all child workflow specific parameters that will be stored inside of a Context.
|
||
|
ChildWorkflowOptions = internal.ChildWorkflowOptions
|
||
|
|
||
|
// RegisterOptions consists of options for registering a workflow
|
||
|
RegisterOptions = internal.RegisterWorkflowOptions
|
||
|
|
||
|
// Info information about currently executing workflow
|
||
|
Info = internal.WorkflowInfo
|
||
|
|
||
|
// ContinueAsNewError can be returned by a workflow implementation function and indicates that
|
||
|
// the workflow should continue as new with the same WorkflowID, but new RunID and new history.
|
||
|
ContinueAsNewError = internal.ContinueAsNewError
|
||
|
)
|
||
|
|
||
|
// ExecuteActivity requests activity execution in the context of a workflow.
|
||
|
// Context can be used to pass the settings for this activity.
|
||
|
// For example: task queue that this need to be routed, timeouts that need to be configured.
|
||
|
// Use ActivityOptions to pass down the options.
|
||
|
//
|
||
|
// ao := ActivityOptions{
|
||
|
// TaskQueue: "exampleTaskQueue",
|
||
|
// ScheduleToStartTimeout: 10 * time.Second,
|
||
|
// StartToCloseTimeout: 5 * time.Second,
|
||
|
// ScheduleToCloseTimeout: 10 * time.Second,
|
||
|
// HeartbeatTimeout: 0,
|
||
|
// }
|
||
|
// ctx := WithActivityOptions(ctx, ao)
|
||
|
//
|
||
|
// Or to override a single option
|
||
|
//
|
||
|
// ctx := WithTaskQueue(ctx, "exampleTaskQueue")
|
||
|
//
|
||
|
// Input activity is either an activity name (string) or a function representing an activity that is getting scheduled.
|
||
|
// Note that the function implementation is ignored by this call.
|
||
|
// It uses function to extract activity type string from it.
|
||
|
// Input args are the arguments that need to be passed to the scheduled activity.
|
||
|
// To call an activity that is a member of a structure use the function reference with nil receiver.
|
||
|
// For example if an activity is defined as:
|
||
|
//
|
||
|
// type Activities struct {
|
||
|
// ... // members
|
||
|
// }
|
||
|
//
|
||
|
// func (a *Activities) Activity1() (string, error) {
|
||
|
// ...
|
||
|
// }
|
||
|
//
|
||
|
// Then a workflow can invoke it as:
|
||
|
//
|
||
|
// var a *Activities
|
||
|
// workflow.ExecuteActivity(ctx, a.Activity1)
|
||
|
//
|
||
|
// If the activity failed to complete then the future get error would indicate the failure.
|
||
|
// The error will be of type *ActivityError. It will have important activity information and actual error that caused
|
||
|
// activity failure. Use errors.Unwrap to get this error or errors.As to check it type which can be one of
|
||
|
// *ApplicationError, *TimeoutError, *CanceledError, or *PanicError.
|
||
|
//
|
||
|
// You can cancel the pending activity using context(workflow.WithCancel(ctx)) and that will fail the activity with
|
||
|
// *CanceledError set as cause for *ActivityError. The context in the activity only becomes aware of the cancellation
|
||
|
// when a heartbeat is sent to the server. Since heartbeats may be batched internally, this could take up to the
|
||
|
// HeartbeatTimeout to appear or several minutes by default if that value is not set.
|
||
|
//
|
||
|
// ExecuteActivity immediately returns a Future that can be used to block waiting for activity result or failure.
|
||
|
func ExecuteActivity(ctx Context, activity interface{}, args ...interface{}) Future {
|
||
|
return internal.ExecuteActivity(ctx, activity, args...)
|
||
|
}
|
||
|
|
||
|
// ExecuteLocalActivity requests to run a local activity. A local activity is like a regular activity with some key
|
||
|
// differences:
|
||
|
//
|
||
|
// • Local activity is scheduled and run by the workflow worker locally.
|
||
|
//
|
||
|
// • Local activity does not need Temporal server to schedule activity task and does not rely on activity worker.
|
||
|
//
|
||
|
// • No need to register local activity.
|
||
|
//
|
||
|
// • Local activity is for short living activities (usually finishes within seconds).
|
||
|
//
|
||
|
// • Local activity cannot heartbeat.
|
||
|
//
|
||
|
// Context can be used to pass the settings for this local activity.
|
||
|
// For now there is only one setting for timeout to be set:
|
||
|
// lao := LocalActivityOptions{
|
||
|
// ScheduleToCloseTimeout: 5 * time.Second,
|
||
|
// }
|
||
|
// ctx := WithLocalActivityOptions(ctx, lao)
|
||
|
// The timeout here should be relative shorter than the WorkflowTaskTimeout of the workflow. If you need a
|
||
|
// longer timeout, you probably should not use local activity and instead should use regular activity. Local activity is
|
||
|
// designed to be used for short living activities (usually finishes within seconds).
|
||
|
//
|
||
|
// Input args are the arguments that will to be passed to the local activity. The input args will be hand over directly
|
||
|
// to local activity function without serialization/deserialization because we don't need to pass the input across process
|
||
|
// boundary. However, the result will still go through serialization/deserialization because we need to record the result
|
||
|
// as history to temporal server so if the workflow crashes, a different worker can replay the history without running
|
||
|
// the local activity again.
|
||
|
//
|
||
|
// If the activity failed to complete then the future get error would indicate the failure.
|
||
|
// The error will be of type *ActivityError. It will have important activity information and actual error that caused
|
||
|
// activity failure. Use errors.Unwrap to get this error or errors.As to check it type which can be one of
|
||
|
// *ApplicationError, *TimeoutError, *CanceledError, or *PanicError.
|
||
|
//
|
||
|
// You can cancel the pending activity using context(workflow.WithCancel(ctx)) and that will fail the activity with
|
||
|
// *CanceledError set as cause for *ActivityError.
|
||
|
//
|
||
|
// ExecuteLocalActivity returns Future with local activity result or failure.
|
||
|
func ExecuteLocalActivity(ctx Context, activity interface{}, args ...interface{}) Future {
|
||
|
return internal.ExecuteLocalActivity(ctx, activity, args...)
|
||
|
}
|
||
|
|
||
|
// ExecuteChildWorkflow requests child workflow execution in the context of a workflow.
|
||
|
// Context can be used to pass the settings for the child workflow.
|
||
|
// For example: task queue that this child workflow should be routed, timeouts that need to be configured.
|
||
|
// Use ChildWorkflowOptions to pass down the options.
|
||
|
// cwo := ChildWorkflowOptions{
|
||
|
// WorkflowExecutionTimeout: 10 * time.Minute,
|
||
|
// WorkflowTaskTimeout: time.Minute,
|
||
|
// }
|
||
|
// ctx := WithChildOptions(ctx, cwo)
|
||
|
// Input childWorkflow is either a workflow name or a workflow function that is getting scheduled.
|
||
|
// Input args are the arguments that need to be passed to the child workflow function represented by childWorkflow.
|
||
|
//
|
||
|
// If the child workflow failed to complete then the future get error would indicate the failure.
|
||
|
// The error will be of type *ChildWorkflowExecutionError. It will have important child workflow information and actual error that caused
|
||
|
// child workflow failure. Use errors.Unwrap to get this error or errors.As to check it type which can be one of
|
||
|
// *ApplicationError, *TimeoutError, or *CanceledError.
|
||
|
//
|
||
|
// You can cancel the pending child workflow using context(workflow.WithCancel(ctx)) and that will fail the workflow with
|
||
|
// *CanceledError set as cause for *ChildWorkflowExecutionError.
|
||
|
//
|
||
|
// ExecuteChildWorkflow returns ChildWorkflowFuture.
|
||
|
func ExecuteChildWorkflow(ctx Context, childWorkflow interface{}, args ...interface{}) ChildWorkflowFuture {
|
||
|
return internal.ExecuteChildWorkflow(ctx, childWorkflow, args...)
|
||
|
}
|
||
|
|
||
|
// GetInfo extracts info of a current workflow from a context.
|
||
|
func GetInfo(ctx Context) *Info {
|
||
|
return internal.GetWorkflowInfo(ctx)
|
||
|
}
|
||
|
|
||
|
// GetLogger returns a logger to be used in workflow's context
|
||
|
func GetLogger(ctx Context) log.Logger {
|
||
|
return internal.GetLogger(ctx)
|
||
|
}
|
||
|
|
||
|
// GetMetricsHandler returns a metrics handler to be used in workflow's context.
|
||
|
// This handler does not record metrics during replay.
|
||
|
func GetMetricsHandler(ctx Context) metrics.Handler {
|
||
|
return internal.GetMetricsHandler(ctx)
|
||
|
}
|
||
|
|
||
|
// RequestCancelExternalWorkflow can be used to request cancellation of an external workflow.
|
||
|
// Input workflowID is the workflow ID of target workflow.
|
||
|
// Input runID indicates the instance of a workflow. Input runID is optional (default is ""). When runID is not specified,
|
||
|
// then the currently running instance of that workflowID will be used.
|
||
|
// By default, the current workflow's namespace will be used as target namespace. However, you can specify a different namespace
|
||
|
// of the target workflow using the context like:
|
||
|
// ctx := WithWorkflowNamespace(ctx, "namespace")
|
||
|
// RequestCancelExternalWorkflow return Future with failure or empty success result.
|
||
|
func RequestCancelExternalWorkflow(ctx Context, workflowID, runID string) Future {
|
||
|
return internal.RequestCancelExternalWorkflow(ctx, workflowID, runID)
|
||
|
}
|
||
|
|
||
|
// SignalExternalWorkflow can be used to send signal info to an external workflow.
|
||
|
// Input workflowID is the workflow ID of target workflow.
|
||
|
// Input runID indicates the instance of a workflow. Input runID is optional (default is ""). When runID is not specified,
|
||
|
// then the currently running instance of that workflowID will be used.
|
||
|
// By default, the current workflow's namespace will be used as target namespace. However, you can specify a different namespace
|
||
|
// of the target workflow using the context like:
|
||
|
// ctx := WithWorkflowNamespace(ctx, "namespace")
|
||
|
// SignalExternalWorkflow return Future with failure or empty success result.
|
||
|
func SignalExternalWorkflow(ctx Context, workflowID, runID, signalName string, arg interface{}) Future {
|
||
|
return internal.SignalExternalWorkflow(ctx, workflowID, runID, signalName, arg)
|
||
|
}
|
||
|
|
||
|
// GetSignalChannel returns channel corresponding to the signal name.
|
||
|
func GetSignalChannel(ctx Context, signalName string) ReceiveChannel {
|
||
|
return internal.GetSignalChannel(ctx, signalName)
|
||
|
}
|
||
|
|
||
|
// SideEffect executes the provided function once, records its result into the workflow history. The recorded result on
|
||
|
// history will be returned without executing the provided function during replay. This guarantees the deterministic
|
||
|
// requirement for workflow as the exact same result will be returned in replay.
|
||
|
// Common use case is to run some short non-deterministic code in workflow, like getting random number or new UUID.
|
||
|
// The only way to fail SideEffect is to panic which causes workflow task failure. The workflow task after timeout is
|
||
|
// rescheduled and re-executed giving SideEffect another chance to succeed.
|
||
|
//
|
||
|
// Caution: do not use SideEffect to modify closures. Always retrieve result from SideEffect's encoded return value.
|
||
|
// For example this code is BROKEN:
|
||
|
// // Bad example:
|
||
|
// var random int
|
||
|
// workflow.SideEffect(func(ctx workflow.Context) interface{} {
|
||
|
// random = rand.Intn(100)
|
||
|
// return nil
|
||
|
// })
|
||
|
// // random will always be 0 in replay, thus this code is non-deterministic
|
||
|
// if random < 50 {
|
||
|
// ....
|
||
|
// } else {
|
||
|
// ....
|
||
|
// }
|
||
|
// On replay the provided function is not executed, the random will always be 0, and the workflow could takes a
|
||
|
// different path breaking the determinism.
|
||
|
//
|
||
|
// Here is the correct way to use SideEffect:
|
||
|
// // Good example:
|
||
|
// encodedRandom := SideEffect(func(ctx workflow.Context) interface{} {
|
||
|
// return rand.Intn(100)
|
||
|
// })
|
||
|
// var random int
|
||
|
// encodedRandom.Get(&random)
|
||
|
// if random < 50 {
|
||
|
// ....
|
||
|
// } else {
|
||
|
// ....
|
||
|
// }
|
||
|
func SideEffect(ctx Context, f func(ctx Context) interface{}) converter.EncodedValue {
|
||
|
return internal.SideEffect(ctx, f)
|
||
|
}
|
||
|
|
||
|
// MutableSideEffect executes the provided function once, then it looks up the history for the value with the given id.
|
||
|
// If there is no existing value, then it records the function result as a value with the given id on history;
|
||
|
// otherwise, it compares whether the existing value from history has changed from the new function result by calling the
|
||
|
// provided equals function. If they are equal, it returns the value without recording a new one in history;
|
||
|
// otherwise, it records the new value with the same id on history.
|
||
|
//
|
||
|
// Caution: do not use MutableSideEffect to modify closures. Always retrieve result from MutableSideEffect's encoded
|
||
|
// return value.
|
||
|
//
|
||
|
// The difference between MutableSideEffect() and SideEffect() is that every new SideEffect() call in non-replay will
|
||
|
// result in a new marker being recorded on history. However, MutableSideEffect() only records a new marker if the value
|
||
|
// changed. During replay, MutableSideEffect() will not execute the function again, but it will return the exact same
|
||
|
// value as it was returning during the non-replay run.
|
||
|
//
|
||
|
// One good use case of MutableSideEffect() is to access dynamically changing config without breaking determinism.
|
||
|
func MutableSideEffect(ctx Context, id string, f func(ctx Context) interface{}, equals func(a, b interface{}) bool) converter.EncodedValue {
|
||
|
return internal.MutableSideEffect(ctx, id, f, equals)
|
||
|
}
|
||
|
|
||
|
// DefaultVersion is a version returned by GetVersion for code that wasn't versioned before
|
||
|
const DefaultVersion Version = internal.DefaultVersion
|
||
|
|
||
|
// GetVersion is used to safely perform backwards incompatible changes to workflow definitions.
|
||
|
// It is not allowed to update workflow code while there are workflows running as it is going to break
|
||
|
// determinism. The solution is to have both old code that is used to replay existing workflows
|
||
|
// as well as the new one that is used when it is executed for the first time.
|
||
|
// GetVersion returns maxSupported version when is executed for the first time. This version is recorded into the
|
||
|
// workflow history as a marker event. Even if maxSupported version is changed the version that was recorded is
|
||
|
// returned on replay. DefaultVersion constant contains version of code that wasn't versioned before.
|
||
|
// For example initially workflow has the following code:
|
||
|
// err = workflow.ExecuteActivity(ctx, foo).Get(ctx, nil)
|
||
|
// it should be updated to
|
||
|
// err = workflow.ExecuteActivity(ctx, bar).Get(ctx, nil)
|
||
|
// The backwards compatible way to execute the update is
|
||
|
// v := GetVersion(ctx, "fooChange", DefaultVersion, 1)
|
||
|
// if v == DefaultVersion {
|
||
|
// err = workflow.ExecuteActivity(ctx, foo).Get(ctx, nil)
|
||
|
// } else {
|
||
|
// err = workflow.ExecuteActivity(ctx, bar).Get(ctx, nil)
|
||
|
// }
|
||
|
//
|
||
|
// Then bar has to be changed to baz:
|
||
|
// v := GetVersion(ctx, "fooChange", DefaultVersion, 2)
|
||
|
// if v == DefaultVersion {
|
||
|
// err = workflow.ExecuteActivity(ctx, foo).Get(ctx, nil)
|
||
|
// } else if v == 1 {
|
||
|
// err = workflow.ExecuteActivity(ctx, bar).Get(ctx, nil)
|
||
|
// } else {
|
||
|
// err = workflow.ExecuteActivity(ctx, baz).Get(ctx, nil)
|
||
|
// }
|
||
|
//
|
||
|
// Later when there are no workflow executions running DefaultVersion the correspondent branch can be removed:
|
||
|
// v := GetVersion(ctx, "fooChange", 1, 2)
|
||
|
// if v == 1 {
|
||
|
// err = workflow.ExecuteActivity(ctx, bar).Get(ctx, nil)
|
||
|
// } else {
|
||
|
// err = workflow.ExecuteActivity(ctx, baz).Get(ctx, nil)
|
||
|
// }
|
||
|
//
|
||
|
// It is recommended to keep the GetVersion() call even if single branch is left:
|
||
|
// GetVersion(ctx, "fooChange", 2, 2)
|
||
|
// err = workflow.ExecuteActivity(ctx, baz).Get(ctx, nil)
|
||
|
//
|
||
|
// The reason to keep it is: 1) it ensures that if there is older version execution still running, it will fail here
|
||
|
// and not proceed; 2) if you ever need to make more changes for “fooChange”, for example change activity from baz to qux,
|
||
|
// you just need to update the maxVersion from 2 to 3.
|
||
|
//
|
||
|
// Note that, you only need to preserve the first call to GetVersion() for each changeID. All subsequent call to GetVersion()
|
||
|
// with same changeID are safe to remove. However, if you really want to get rid of the first GetVersion() call as well,
|
||
|
// you can do so, but you need to make sure: 1) all older version executions are completed; 2) you can no longer use “fooChange”
|
||
|
// 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
|
||
|
// different changeID like “fooChange-fix2”, and start minVersion from DefaultVersion again. The code would looks like:
|
||
|
//
|
||
|
// v := workflow.GetVersion(ctx, "fooChange-fix2", workflow.DefaultVersion, 1)
|
||
|
// if v == workflow.DefaultVersion {
|
||
|
// err = workflow.ExecuteActivity(ctx, baz, data).Get(ctx, nil)
|
||
|
// } else {
|
||
|
// err = workflow.ExecuteActivity(ctx, qux, data).Get(ctx, nil)
|
||
|
// }
|
||
|
func GetVersion(ctx Context, changeID string, minSupported, maxSupported Version) Version {
|
||
|
return internal.GetVersion(ctx, changeID, minSupported, maxSupported)
|
||
|
}
|
||
|
|
||
|
// SetQueryHandler sets the query handler to handle workflow query. The queryType specify which query type this handler
|
||
|
// should handle. The handler must be a function that returns 2 values. The first return value must be a serializable
|
||
|
// result. The second return value must be an error. The handler function could receive any number of input parameters.
|
||
|
// All the input parameter must be serializable. You should call workflow.SetQueryHandler() at the beginning of the workflow
|
||
|
// code. When client calls Client.QueryWorkflow() to temporal server, a task will be generated on server that will be dispatched
|
||
|
// to a workflow worker, which will replay the history events and then execute a query handler based on the query type.
|
||
|
// The query handler will be invoked out of the context of the workflow, meaning that the handler code must not use workflow
|
||
|
// context to do things like workflow.NewChannel(), workflow.Go() or to call any workflow blocking functions like
|
||
|
// Channel.Get() or Future.Get(). Trying to do so in query handler code will fail the query and client will receive
|
||
|
// QueryFailedError.
|
||
|
// Example of workflow code that support query type "current_state":
|
||
|
// func MyWorkflow(ctx workflow.Context, input string) error {
|
||
|
// currentState := "started" // this could be any serializable struct
|
||
|
// err := workflow.SetQueryHandler(ctx, "current_state", func() (string, error) {
|
||
|
// return currentState, nil
|
||
|
// })
|
||
|
// if err != nil {
|
||
|
// currentState = "failed to register query handler"
|
||
|
// return err
|
||
|
// }
|
||
|
// // your normal workflow code begins here, and you update the currentState as the code makes progress.
|
||
|
// currentState = "waiting timer"
|
||
|
// err = NewTimer(ctx, time.Hour).Get(ctx, nil)
|
||
|
// if err != nil {
|
||
|
// currentState = "timer failed"
|
||
|
// return err
|
||
|
// }
|
||
|
//
|
||
|
// currentState = "waiting activity"
|
||
|
// ctx = WithActivityOptions(ctx, myActivityOptions)
|
||
|
// err = ExecuteActivity(ctx, MyActivity, "my_input").Get(ctx, nil)
|
||
|
// if err != nil {
|
||
|
// currentState = "activity failed"
|
||
|
// return err
|
||
|
// }
|
||
|
// currentState = "done"
|
||
|
// return nil
|
||
|
// }
|
||
|
func SetQueryHandler(ctx Context, queryType string, handler interface{}) error {
|
||
|
return internal.SetQueryHandler(ctx, queryType, handler)
|
||
|
}
|
||
|
|
||
|
// IsReplaying returns whether the current workflow code is replaying.
|
||
|
//
|
||
|
// Warning! Never make commands, like schedule activity/childWorkflow/timer or send/wait on future/channel, based on
|
||
|
// this flag as it is going to break workflow determinism requirement.
|
||
|
// The only reasonable use case for this flag is to avoid some external actions during replay, like custom logging or
|
||
|
// metric reporting. Please note that Temporal already provide standard logging/metric via workflow.GetLogger(ctx) and
|
||
|
// workflow.GetMetricsHandler(ctx), and those standard mechanism are replay-aware and it will automatically suppress
|
||
|
// during replay. Only use this flag if you need custom logging/metrics reporting, for example if you want to log to
|
||
|
// kafka.
|
||
|
//
|
||
|
// Warning! Any action protected by this flag should not fail or if it does fail should ignore that failure or panic
|
||
|
// on the failure. If workflow don't want to be blocked on those failure, it should ignore those failure; if workflow do
|
||
|
// want to make sure it proceed only when that action succeed then it should panic on that failure. Panic raised from a
|
||
|
// workflow causes workflow task to fail and temporal server will rescheduled later to retry.
|
||
|
func IsReplaying(ctx Context) bool {
|
||
|
return internal.IsReplaying(ctx)
|
||
|
}
|
||
|
|
||
|
// HasLastCompletionResult checks if there is completion result from previous runs.
|
||
|
// This is used in combination with cron schedule. A workflow can be started with an optional cron schedule.
|
||
|
// If a cron workflow wants to pass some data to next schedule, it can return any data and that data will become
|
||
|
// available when next run starts.
|
||
|
// This HasLastCompletionResult() checks if there is such data available passing down from previous successful run.
|
||
|
func HasLastCompletionResult(ctx Context) bool {
|
||
|
return internal.HasLastCompletionResult(ctx)
|
||
|
}
|
||
|
|
||
|
// GetLastCompletionResult extract last completion result from previous run for this cron workflow.
|
||
|
// This is used in combination with cron schedule. A workflow can be started with an optional cron schedule.
|
||
|
// If a cron workflow wants to pass some data to next schedule, it can return any data and that data will become
|
||
|
// available when next run starts.
|
||
|
// This GetLastCompletionResult() extract the data into expected data structure.
|
||
|
// See TestWorkflowEnvironment.SetLastCompletionResult() for unit test support.
|
||
|
func GetLastCompletionResult(ctx Context, d ...interface{}) error {
|
||
|
return internal.GetLastCompletionResult(ctx, d...)
|
||
|
}
|
||
|
|
||
|
// GetLastError extracts the latest failure from any from previous run for this workflow, if one has failed. If none
|
||
|
// have failed, nil is returned.
|
||
|
//
|
||
|
// See TestWorkflowEnvironment.SetLastError() for unit test support.
|
||
|
func GetLastError(ctx Context) error {
|
||
|
return internal.GetLastError(ctx)
|
||
|
}
|
||
|
|
||
|
// UpsertSearchAttributes is used to add or update workflow search attributes.
|
||
|
// The search attributes can be used in query of List/Scan/Count workflow APIs.
|
||
|
// The key and value type must be registered on temporal server side;
|
||
|
// The value has to deterministic when replay;
|
||
|
// The value has to be Json serializable.
|
||
|
// UpsertSearchAttributes will merge attributes to existing map in workflow, for example workflow code:
|
||
|
// func MyWorkflow(ctx workflow.Context, input string) error {
|
||
|
// attr1 := map[string]interface{}{
|
||
|
// "CustomIntField": 1,
|
||
|
// "CustomBoolField": true,
|
||
|
// }
|
||
|
// workflow.UpsertSearchAttributes(ctx, attr1)
|
||
|
//
|
||
|
// attr2 := map[string]interface{}{
|
||
|
// "CustomIntField": 2,
|
||
|
// "CustomKeywordField": "seattle",
|
||
|
// }
|
||
|
// workflow.UpsertSearchAttributes(ctx, attr2)
|
||
|
// }
|
||
|
// will eventually have search attributes:
|
||
|
// map[string]interface{}{
|
||
|
// "CustomIntField": 2,
|
||
|
// "CustomBoolField": true,
|
||
|
// "CustomKeywordField": "seattle",
|
||
|
// }
|
||
|
// This is only supported when using ElasticSearch.
|
||
|
func UpsertSearchAttributes(ctx Context, attributes map[string]interface{}) error {
|
||
|
return internal.UpsertSearchAttributes(ctx, attributes)
|
||
|
}
|
||
|
|
||
|
// NewContinueAsNewError creates ContinueAsNewError instance
|
||
|
// If the workflow main function returns this error then the current execution is ended and
|
||
|
// the new execution with same workflow ID is started automatically with options
|
||
|
// provided to this function.
|
||
|
// ctx - use context to override any options for the new workflow like execution timeout, workflow task timeout, task queue.
|
||
|
// if not mentioned it would use the defaults that the current workflow is using.
|
||
|
// ctx := WithWorkflowExecutionTimeout(ctx, 30 * time.Minute)
|
||
|
// ctx := WithWorkflowTaskTimeout(ctx, time.Minute)
|
||
|
// ctx := WithWorkflowTaskQueue(ctx, "example-group")
|
||
|
// wfn - workflow function. for new execution it can be different from the currently running.
|
||
|
// args - arguments for the new workflow.
|
||
|
//
|
||
|
func NewContinueAsNewError(ctx Context, wfn interface{}, args ...interface{}) error {
|
||
|
return internal.NewContinueAsNewError(ctx, wfn, args...)
|
||
|
}
|
||
|
|
||
|
// IsContinueAsNewError return if the err is a ContinueAsNewError
|
||
|
func IsContinueAsNewError(err error) bool {
|
||
|
var continueAsNewErr *ContinueAsNewError
|
||
|
return errors.As(err, &continueAsNewErr)
|
||
|
}
|