Documentation
¶
Overview ¶
Package workflow defines typed workflow definitions, runs, step checkpointing, and the workflow store interface.
Workflows are durable, multi-step functions. They survive process restarts by checkpointing completed steps. A step that already completed is skipped on resume; only the failed or pending step re-executes.
Defining a Workflow ¶
var ProcessOrder = workflow.NewWorkflow("process_order",
func(wf *workflow.Workflow, input OrderInput) error {
if err := wf.Step("validate", func(ctx context.Context) error {
return validateOrder(ctx, input)
}); err != nil {
return err
}
if err := wf.Step("charge", func(ctx context.Context) error {
return chargeCard(ctx, input.PaymentToken, input.Amount)
}); err != nil {
return err
}
return wf.Step("fulfill", func(ctx context.Context) error {
return fulfillOrder(ctx, input.OrderID)
})
},
)
Parallel Steps ¶
Use Workflow.Parallel to run independent steps concurrently. The first argument names the group, so the checkpoint survives a restart:
err := wf.Parallel("notify",
func(ctx context.Context) error { return sendConfirmationEmail(ctx, input.Email) },
func(ctx context.Context) error { return updateInventory(ctx, input.Items) },
)
Waiting for Events ¶
A workflow can pause until an external event arrives:
evt, err := wf.WaitForEvent("payment.confirmed", 24*time.Hour)
State Machine ¶
A Run moves through these states:
running -> completed running -> failed
Key Types ¶
- Definition: typed workflow descriptor with Name and Handler
- Run: a single workflow execution record
- RunState: running, completed, or failed
- Registry: maps workflow names to type-erased runner functions
Package workflow defines workflow definitions, runs, steps, checkpoints, and the workflow store interface.
Index ¶
- Variables
- func CompareCheckpoints(a, b *Checkpoint) int
- func FanOut[T, R any](w *Workflow, name string, inputs []T) (_ []R, err error)
- func RegisterDefinition[T any](r *Registry, def *Definition[T])
- func RunChild[T, R any](w *Workflow, name string, input T) (_ R, err error)
- func SpawnChild[T any](w *Workflow, name string, input T) (_ id.RunID, err error)
- func StepWithResult[T any](w *Workflow, name string, fn func(ctx context.Context) (T, error)) (_ T, err error)
- func StepWithResultAndCompensation[T any](w *Workflow, name string, execute func(ctx context.Context) (T, error), ...) (T, error)
- type Checkpoint
- type ChildStarter
- type Compensation
- type CountRunsOpts
- type Definition
- type ListOpts
- type ListRunsPageOpts
- type PageLister
- type Registry
- type Reopener
- type ReplayPlan
- type Run
- type RunEmitter
- type RunGetter
- type RunPage
- type RunState
- type Runner
- func (r *Runner) GetTimeline(ctx context.Context, runID id.RunID) ([]TimelineEntry, error)
- func (r *Runner) InspectStep(ctx context.Context, runID id.RunID, stepName string) ([]byte, error)
- func (r *Runner) MigrateRun(ctx context.Context, runID id.RunID, toVersion int) error
- func (r *Runner) PlanReplay(ctx context.Context, runID id.RunID, fromStep string) (*ReplayPlan, error)
- func (r *Runner) Registry() *Registry
- func (r *Runner) ReplayFrom(ctx context.Context, runID id.RunID, fromStep string) (*ReplayPlan, error)
- func (r *Runner) ReplayFromGeneration(ctx context.Context, runID id.RunID, fromStep string, generation int64) (*ReplayPlan, error)
- func (r *Runner) Resume(ctx context.Context, runID id.RunID) error
- func (r *Runner) ResumeAll(ctx context.Context) error
- func (r *Runner) Shutdown(ctx context.Context) error
- func (r *Runner) SpawnChildRaw(ctx context.Context, parentRunID id.RunID, name string, input []byte) (*Run, error)
- func (r *Runner) StartChildRaw(ctx context.Context, parentRunID id.RunID, name string, input []byte) (*Run, error)
- func (r *Runner) StartRaw(ctx context.Context, name string, input []byte) (*Run, error)
- func (r *Runner) WithTelemetry(t *telemetry.Telemetry) *Runner
- type RunnerFunc
- type StepEmitter
- type Store
- type TimelineEntry
- type Workflow
- func (w *Workflow) Compensations() []Compensation
- func (w *Workflow) Context() context.Context
- func (w *Workflow) Parallel(groupName string, steps ...func(ctx context.Context) error) (err error)
- func (w *Workflow) Run() *Run
- func (w *Workflow) RunCompensations() error
- func (w *Workflow) RunID() id.RunID
- func (w *Workflow) SetChildStarter(cs ChildStarter)
- func (w *Workflow) Sleep(name string, d time.Duration) (err error)
- func (w *Workflow) Step(name string, fn func(ctx context.Context) error) (err error)
- func (w *Workflow) StepWithCompensation(name string, execute func(ctx context.Context) error, ...) error
- func (w *Workflow) WaitForAll(names []string, timeout time.Duration) (_ []*event.Event, err error)
- func (w *Workflow) WaitForAny(names []string, timeout time.Duration) (_ *event.Event, err error)
- func (w *Workflow) WaitForEvent(name string, timeout time.Duration) (_ *event.Event, err error)
- func (w *Workflow) WaitForMatch(name string, timeout time.Duration, match func(*event.Event) bool) (_ *event.Event, err error)
Constants ¶
This section is empty.
Variables ¶
var ErrRunnerShutdown = errors.New("workflow: runner is shut down")
ErrRunnerShutdown is what ReplayFrom returns once Shutdown has begun. The refusal comes before the run is claimed, so the run is left exactly as it was.
Functions ¶
func CompareCheckpoints ¶
func CompareCheckpoints(a, b *Checkpoint) int
CompareCheckpoints orders checkpoints by creation time, then ID. Timeline, replay preview and storage pruning use the same tie boundary.
func FanOut ¶
FanOut starts N child workflows in parallel and collects all results. Each child is independently checkpointed via RunChild. If any child fails, the remaining are cancelled and the error is returned.
func RegisterDefinition ¶
func RegisterDefinition[T any](r *Registry, def *Definition[T])
RegisterDefinition registers a typed workflow definition. The generic handler is wrapped in a closure that JSON-unmarshals the input into T before calling the typed handler.
If Version is 0 (default), it is treated as version 1. Multiple versions of the same workflow name can coexist.
This is a package-level generic function because Go does not allow generic methods on non-generic receiver types.
func RunChild ¶
RunChild starts a child workflow and blocks until it completes, returning the decoded result. The child run is linked to the parent via ParentRunID and the result is checkpointed for crash recovery.
T is the input type, R is the result type (decoded from the child's JSON output).
func SpawnChild ¶
SpawnChild starts a child workflow asynchronously (fire-and-forget) and returns the child's run ID. The child run is linked to the parent via ParentRunID and executes in a background goroutine. Use the Store's GetRun to check on its status later.
func StepWithResult ¶
func StepWithResult[T any](w *Workflow, name string, fn func(ctx context.Context) (T, error)) (_ T, err error)
StepWithResult executes a named step that returns a typed value. The result is serialized via encoding/gob and saved as a checkpoint. On resume, the cached result is deserialized and returned without re-executing the step function.
This is a package-level generic function because Go does not allow generic methods on non-generic receiver types.
func StepWithResultAndCompensation ¶
func StepWithResultAndCompensation[T any]( w *Workflow, name string, execute func(ctx context.Context) (T, error), compensate func(ctx context.Context) error, ) (T, error)
StepWithResultAndCompensation executes a named step that returns a typed value, with an associated compensation function. If the step succeeds, the result is checkpointed and the compensation is registered on the stack.
Types ¶
type Checkpoint ¶
type Checkpoint struct {
ID id.CheckpointID `json:"id"`
RunID id.RunID `json:"run_id"`
StepName string `json:"step_name"`
Data []byte `json:"data"`
CreatedAt time.Time `json:"created_at"`
}
Checkpoint stores the serialized state of a completed workflow step, enabling crash recovery by replaying from the last checkpoint.
type ChildStarter ¶
type ChildStarter interface {
// StartChildRaw starts a child workflow synchronously, blocking until it completes.
// The child run's ParentRunID is set to the given parent.
StartChildRaw(ctx context.Context, parentRunID id.RunID, name string, input []byte) (*Run, error)
// SpawnChildRaw starts a child workflow asynchronously, returning immediately
// after the run is persisted. The child executes in a background goroutine.
SpawnChildRaw(ctx context.Context, parentRunID id.RunID, name string, input []byte) (*Run, error)
}
ChildStarter is the interface used by the Workflow to start child workflow runs. It is satisfied by the workflow.Runner, injected by the engine layer to break import cycles.
type Compensation ¶
Compensation represents a deferred undo action for a completed step.
type CountRunsOpts ¶
CountRunsOpts filters CountRuns. Name is an exact workflow name; empty State or Name counts every value.
func (CountRunsOpts) Match ¶
func (o CountRunsOpts) Match(r *Run) bool
Match reports whether r is counted under o.
type Definition ¶
type Definition[T any] struct { // Name is the unique identifier for this workflow type. Name string // Version is the definition version (default 0 = version 1). // Multiple versions of the same workflow can be registered simultaneously. // New runs use the latest version; existing runs continue on their version. Version int // Handler is the function that executes the workflow logic. // It receives a *Workflow which provides Step, Parallel, // WaitForEvent, and Sleep methods. Handler func(wf *Workflow, input T) error }
Definition is a typed workflow definition with a handler function. T is the input type (must be JSON-serializable for Run.Input storage).
func NewWorkflow ¶
func NewWorkflow[T any](name string, handler func(wf *Workflow, input T) error) *Definition[T]
NewWorkflow creates a typed workflow definition (version 1).
func NewWorkflowV ¶
func NewWorkflowV[T any](name string, version int, handler func(wf *Workflow, input T) error) *Definition[T]
NewWorkflowV creates a typed workflow definition with an explicit version.
type ListOpts ¶
type ListOpts struct {
// Limit is the maximum number of runs to return. Zero means no limit.
Limit int
// Offset is the number of runs to skip.
Offset int
// State filters by run state. Empty means all states.
State RunState
}
ListOpts controls pagination for workflow run list queries.
type ListRunsPageOpts ¶
type ListRunsPageOpts struct {
State RunState
NamePrefix string
ScopeAppID string
ScopeOrgID string
Cursor string
Limit int
}
ListRunsPageOpts filters and pages ListRunsPage. The rules are the same as job.ListJobsOpts: exact matches except NamePrefix, empty means no filter, newest first by ID.
func (ListRunsPageOpts) Match ¶
func (o ListRunsPageOpts) Match(r *Run) bool
Match reports whether r passes every filter in o.
type PageLister ¶
type PageLister interface {
ListRunsPage(ctx context.Context, opts ListRunsPageOpts) (RunPage, error)
CountRuns(ctx context.Context, opts CountRunsOpts) (int64, error)
}
PageLister is the paged run read and the run count the dashboard uses. ListRuns is kept, unchanged, for the engine's own callers.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry maps workflow names to versioned runner functions. Multiple versions of the same workflow can be registered; the latest version is used for new runs. It is safe for concurrent use.
func (*Registry) Get ¶
func (r *Registry) Get(name string) (RunnerFunc, bool)
Get returns the latest-version runner for the given workflow name. Returns false if no runner is registered.
func (*Registry) GetVersion ¶
func (r *Registry) GetVersion(name string, version int) (RunnerFunc, bool)
GetVersion returns the runner for a specific version of a workflow. If version <= 0, behaves like Get (returns latest).
func (*Registry) LatestVersion ¶
LatestVersion returns the highest registered version number for a workflow. Returns 0 if the workflow is not registered.
type Reopener ¶
type Reopener interface {
// ReopenRun sets state = running, error = "" and completed_at = null
// (and stamps updated_at) only if the run is not already running and
// ReplayGeneration equals expectedGeneration. It increments the generation
// atomically. When the run is running or the generation changed it returns
// an error wrapping dispatch.ErrInvalidState that names the state;
// when the run does not exist, dispatch.ErrRunNotFound. The check and
// the write are one atomic step: of two concurrent reopens, exactly
// one wins.
ReopenRun(ctx context.Context, runID id.RunID, expectedGeneration int64) error
}
Reopener is the conditional claim that makes replay-from-step safe.
Replaying a run from a step re-executes it under the same run ID, so two replays started together would run the tail of the workflow twice, side by side. The claim is the state change itself: only one caller can move a run out of a finished state into running.
type ReplayPlan ¶
type ReplayPlan struct {
RunID id.RunID `json:"run_id"`
FromStep string `json:"from_step"`
// Version is the run's stamped version, the one the replay runs on.
// A run stamped 0 predates versioning and runs on version 1.
Version int `json:"version"`
// Reruns names the checkpointed steps after FromStep whose
// checkpoints the replay deletes, so they run again, in checkpoint
// order. Steps the run never reached are not listed but run too.
Reruns []string `json:"reruns"`
// State is the run's state when the plan was made. ReplayFrom
// refuses a running run, so a caller can refuse it early from here.
State RunState `json:"state"`
// Generation fences the claim against the run used for this plan.
Generation int64 `json:"generation"`
}
ReplayPlan says what replaying a run from a step does. PlanReplay returns it without changing anything; ReplayFrom returns the plan it acted on.
type Run ¶
type Run struct {
dispatch.Entity
ID id.RunID `json:"id"`
Name string `json:"name"`
State RunState `json:"state"`
Input []byte `json:"input,omitempty"`
Output []byte `json:"output,omitempty"`
Error string `json:"error,omitempty"`
ScopeAppID string `json:"scope_app_id,omitempty"`
ScopeOrgID string `json:"scope_org_id,omitempty"`
StartedAt time.Time `json:"started_at"`
CompletedAt *time.Time `json:"completed_at,omitempty"`
// Version is the workflow definition version this run executes.
// Zero means version 1 (unversioned).
Version int `json:"version,omitempty"`
// ReplayGeneration advances on each successful replay claim. A plan
// must still match this generation when it reopens the run.
ReplayGeneration int64 `json:"replay_generation"`
// ParentRunID links this run to its parent (nil for top-level runs).
ParentRunID *id.RunID `json:"parent_run_id,omitempty"`
}
Run represents a single execution of a workflow.
type RunEmitter ¶
type RunEmitter interface {
StepEmitter
EmitWorkflowStarted(ctx context.Context, run *Run)
EmitWorkflowCompleted(ctx context.Context, run *Run, elapsed time.Duration)
EmitWorkflowFailed(ctx context.Context, run *Run, err error)
}
RunEmitter emits workflow-level lifecycle events. This interface is satisfied by ext.Registry (via an adapter in the engine package) to break the import cycle between workflow and ext.
type RunPage ¶
RunPage is one page of ListRunsPage. NextCursor and Complete mean what they mean on job.Page.
type RunState ¶
type RunState string
RunState represents the lifecycle state of a workflow run.
const ( // RunStateRunning means the workflow is currently executing. RunStateRunning RunState = "running" // RunStateCompleted means the workflow finished successfully. RunStateCompleted RunState = "completed" // RunStateFailed means the workflow failed terminally. RunStateFailed RunState = "failed" )
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner orchestrates workflow execution: creating runs, building the Workflow context, invoking handlers, and managing state.
func NewRunner ¶
func NewRunner( registry *Registry, store Store, eventStore event.Store, emitter RunEmitter, logger log.Logger, ) *Runner
NewRunner creates a workflow runner.
func (*Runner) GetTimeline ¶
GetTimeline returns an ordered timeline of all steps executed in a workflow run. Each entry corresponds to a checkpoint, sorted by creation time. This enables point-in-time inspection of workflow state.
func (*Runner) InspectStep ¶
InspectStep returns the raw checkpoint data for a specific step in a workflow run. This allows decoding and examining the result that was saved when the step completed.
func (*Runner) MigrateRun ¶
MigrateRun upgrades a workflow run to a new version. It updates the run's Version field and re-executes on the new handler. Checkpoints are preserved so completed steps are skipped during replay on the new version's handler.
func (*Runner) PlanReplay ¶
func (r *Runner) PlanReplay(ctx context.Context, runID id.RunID, fromStep string) (*ReplayPlan, error)
PlanReplay reports what ReplayFrom would do for the run and step, without changing anything. It fails when the run does not exist (wrapping dispatch.ErrRunNotFound), when fromStep has no checkpoint, or when the run's stamped version is not registered (both wrapping dispatch.ErrInvalidState). A running run plans fine; its State says ReplayFrom would refuse it.
func (*Runner) ReplayFrom ¶
func (r *Runner) ReplayFrom(ctx context.Context, runID id.RunID, fromStep string) (*ReplayPlan, error)
ReplayFrom re-runs a finished run from a step. Checkpoints up to and including fromStep are kept, so those steps are skipped; every later checkpoint is deleted and those steps run again. The run executes on its stamped version, never a newer one registered since.
The refusals are PlanReplay's, plus: a running run, or one another replay claims first, wraps dispatch.ErrInvalidState; once Shutdown has begun, ErrRunnerShutdown. Of two concurrent calls exactly one starts.
The run executes on the runner's background launcher and ReplayFrom returns the plan as soon as it has started. The run keeps the values of ctx but not its cancellation: Shutdown cancels it instead.
func (*Runner) ReplayFromGeneration ¶
func (r *Runner) ReplayFromGeneration(ctx context.Context, runID id.RunID, fromStep string, generation int64) (*ReplayPlan, error)
ReplayFromGeneration requires the generation returned by PlanReplay. A stale confirmation is refused before claiming or pruning the run.
func (*Runner) Resume ¶
Resume resumes a workflow run that was in "running" state (crash recovery). It re-executes the handler; steps with checkpoints are skipped automatically. The run continues on its stamped version (not necessarily the latest).
func (*Runner) ResumeAll ¶
ResumeAll finds all runs in "running" state and resumes them. Called at startup for crash recovery.
func (*Runner) Shutdown ¶
Shutdown stops the background launcher. It refuses every later ReplayFrom with ErrRunnerShutdown, cancels the context the in-flight background runs execute under, and waits for them to return or for ctx to expire, whichever is first. On expiry it returns an error wrapping ctx.Err() and leaves the runs to finish on their own.
A run whose steps honour cancellation fails with the context error. On a durable store the write recording that usually fails too, since it shares the cancelled context, so the run stays running and the next ResumeAll picks it up from its checkpoints.
Shutdown is safe to call more than once; a later call waits again.
func (*Runner) SpawnChildRaw ¶
func (r *Runner) SpawnChildRaw(ctx context.Context, parentRunID id.RunID, name string, input []byte) (*Run, error)
SpawnChildRaw starts a child workflow asynchronously, returning immediately after the run is persisted. The child executes in a background goroutine. Implements ChildStarter.
func (*Runner) StartChildRaw ¶
func (r *Runner) StartChildRaw(ctx context.Context, parentRunID id.RunID, name string, input []byte) (*Run, error)
StartChildRaw starts a child workflow synchronously, blocking until it completes. The child run's ParentRunID is set to the given parent. Implements ChildStarter.
func (*Runner) StartRaw ¶
StartRaw starts a workflow run with pre-serialized JSON input. The run is stamped with the latest registered version.
func (*Runner) WithTelemetry ¶
WithTelemetry sets where the runner records its spans and metrics: a "run workflow" span per run, a "run workflow step" span per step that actually runs (a step replayed from its checkpoint gets none), and the run and step duration histograms. It returns r so callers can chain it onto NewRunner. engine.Build passes the engine's own.
type RunnerFunc ¶
RunnerFunc is a type-erased workflow runner that accepts raw JSON input. The typed Definition[T] is converted to a RunnerFunc at registration time by closing over JSON unmarshal + the typed handler.
type StepEmitter ¶
type StepEmitter interface {
EmitStepCompleted(ctx context.Context, run *Run, stepName string, elapsed time.Duration)
EmitStepFailed(ctx context.Context, run *Run, stepName string, err error)
}
StepEmitter is called by the Workflow to emit step lifecycle events. This interface is satisfied by ext.Registry (via an adapter in the runner package) to break the import cycle between workflow and ext.
type Store ¶
type Store interface {
Reopener
// CreateRun persists a new workflow run.
CreateRun(ctx context.Context, run *Run) error
// GetRun retrieves a workflow run by ID.
GetRun(ctx context.Context, runID id.RunID) (*Run, error)
// UpdateRun persists changes to an existing workflow run. It refuses a
// ReplayGeneration that no longer matches with dispatch.ErrInvalidState,
// so an old execution cannot overwrite a later replay's claim.
UpdateRun(ctx context.Context, run *Run) error
// ListRuns returns workflow runs matching the given options.
ListRuns(ctx context.Context, opts ListOpts) ([]*Run, error)
// SaveCheckpoint persists checkpoint data for a workflow step.
// If a checkpoint already exists for the same run/step, it is replaced.
SaveCheckpoint(ctx context.Context, runID id.RunID, stepName string, data []byte) error
// GetCheckpoint retrieves checkpoint data for a specific workflow step.
// Returns nil data if no checkpoint exists.
GetCheckpoint(ctx context.Context, runID id.RunID, stepName string) ([]byte, error)
// ListCheckpoints returns checkpoints in ascending creation-time/ID order.
ListCheckpoints(ctx context.Context, runID id.RunID) ([]*Checkpoint, error)
// ListChildRuns returns all child workflow runs for a parent.
ListChildRuns(ctx context.Context, parentRunID id.RunID) ([]*Run, error)
// DeleteCheckpointsAfter removes checkpoints strictly after the named
// step in creation-time/ID order, preserving the target and earlier ties.
DeleteCheckpointsAfter(ctx context.Context, runID id.RunID, afterStep string) error
}
Store defines the persistence contract for workflows.
type TimelineEntry ¶
type TimelineEntry struct {
StepName string `json:"step_name"`
Data []byte `json:"data,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
TimelineEntry represents a single step in a workflow's execution history. The timeline is ordered by checkpoint creation time, providing a chronological view of how the workflow executed.
type Workflow ¶
type Workflow struct {
// contains filtered or unexported fields
}
Workflow is the execution context passed to workflow handler functions. It provides durable step execution, parallel fan-out, event waiting, durable sleep, saga compensations, and child workflow composition. Each method automatically checkpoints results to enable crash recovery.
func NewWorkflowContext ¶
func NewWorkflowContext( ctx context.Context, run *Run, store Store, eventStore event.Store, emitter StepEmitter, logger log.Logger, ) *Workflow
NewWorkflowContext creates a new Workflow execution context. This is called by the workflow runner, not by users.
func (*Workflow) Compensations ¶
func (w *Workflow) Compensations() []Compensation
Compensations returns the list of registered compensations (for runner use).
func (*Workflow) Parallel ¶
Parallel executes multiple step functions concurrently using errgroup. If any step fails, the others are cancelled and the first error is returned. Each sub-step gets its own checkpoint with the name "parallel:<group>:<index>". A group-level checkpoint marks overall completion for resume.
func (*Workflow) RunCompensations ¶
RunCompensations executes all registered compensations in reverse order. Each compensation is checkpointed for crash recovery. Called by the runner when a workflow fails.
func (*Workflow) SetChildStarter ¶
func (w *Workflow) SetChildStarter(cs ChildStarter)
SetChildStarter injects the child workflow starter. Called by the runner.
func (*Workflow) Sleep ¶
Sleep pauses the workflow for the specified duration. On crash recovery, if a checkpoint exists for this sleep step, it is skipped immediately. The sleep can be interrupted by context cancellation.
func (*Workflow) Step ¶
Step executes a named step function. If a checkpoint exists for this step name, the step is skipped (idempotent replay). Otherwise the function is executed and a checkpoint is saved on success. On failure, EmitWorkflowStepFailed is called and the error is returned.
func (*Workflow) StepWithCompensation ¶
func (w *Workflow) StepWithCompensation( name string, execute func(ctx context.Context) error, compensate func(ctx context.Context) error, ) error
StepWithCompensation executes a named step with an associated compensation function. If the step succeeds, the compensation is pushed onto a LIFO stack. When the workflow fails later, all registered compensations run in reverse order to undo completed work (saga pattern).
func (*Workflow) WaitForAll ¶
WaitForAll blocks until events matching ALL specified names are received or the timeout expires. Each received event is individually checkpointed. Returns an error if any subscription times out.
func (*Workflow) WaitForAny ¶
WaitForAny blocks until an event matching ANY of the given names is received or the timeout expires. Returns the first event received, or nil on timeout.
func (*Workflow) WaitForEvent ¶
WaitForEvent blocks until an event matching the given name is published or the timeout expires. On success the event is acknowledged and checkpointed. On timeout, an empty checkpoint is saved and nil is returned. On resume from checkpoint, the cached event is returned without re-waiting.
func (*Workflow) WaitForMatch ¶
func (w *Workflow) WaitForMatch( name string, timeout time.Duration, match func(*event.Event) bool, ) (_ *event.Event, err error)
WaitForMatch blocks until an event matching the given name AND predicate is received, or the timeout expires. Events that don't match the predicate are skipped (not acknowledged). Returns nil on timeout.