workflow

package
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 2, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package workflow builds typed directed acyclic graphs of cron jobs and runs them as one cron.Job.

A Builder collects steps: typed functions (Builder.Step) or plain cron.Job values (Builder.Job). After declares a dependency on another step's Output together with the upstream result that must hold (OnSuccess, OnFailure, OnSkipped or OnComplete). Build validates names, dependencies and cycles and freezes the graph into an immutable Workflow. Execute runs ready steps with bounded parallelism, hands each step the successful outputs of its dependencies through Inputs, and reports every step's outcome in an Execution.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrDuplicateStep reports two steps with the same name.
	ErrDuplicateStep = errors.New("workflow: duplicate step")
	// ErrDuplicateDep reports the same dependency declared twice on one step.
	ErrDuplicateDep = errors.New("workflow: duplicate dependency")
	// ErrUnknownDep reports a dependency on a step that is not in the graph,
	// including an Output taken from another Builder.
	ErrUnknownDep = errors.New("workflow: unknown dependency")
	// ErrCycle reports a dependency cycle; the message lists its path.
	ErrCycle = errors.New("workflow: dependency cycle")
	// ErrNilJob reports a step added with a nil function or cron.Job.
	ErrNilJob = errors.New("workflow: step has no job")
	// ErrInvalidName reports an empty step name or one with surrounding
	// whitespace.
	ErrInvalidName = errors.New("workflow: invalid step name")
	// ErrInvalidOption is wrapped when a Builder or step option is nil or
	// rejects its argument.
	ErrInvalidOption = errors.New("workflow: invalid option")
	// ErrBuilderFrozen is returned by Build when steps were added after an
	// earlier Build.
	ErrBuilderFrozen = errors.New("workflow: builder is frozen")
	// ErrNilContext is reported by Execution.Err when Execute received a nil
	// context; every step is skipped.
	ErrNilContext = errors.New("workflow: nil context")
)

Sentinel errors reported by Build and Execute; match them with errors.Is.

Functions

This section is empty.

Types

type Builder added in v0.5.3

type Builder struct {
	// contains filtered or unexported fields
}

Builder incrementally constructs one Workflow. Errors from options and steps are collected and reported by Build, so a sequence of Step calls needs no error handling. Build freezes the Builder: later Step and Job calls are ignored and make every subsequent Build return ErrBuilderFrozen. Builder is not safe for concurrent use; its zero value is ready to use.

Example

ExampleBuilder wires two typed steps; the second reads the first's output.

package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/libtnb/cron/workflow"
)

func main() {
	b := workflow.New(workflow.WithMaxParallelism(4))

	fetch := b.Step[int]("fetch", func(ctx context.Context, _ workflow.Inputs) (int, error) {
		return 42, nil
	})
	store := b.Step[string]("store", func(ctx context.Context, in workflow.Inputs) (string, error) {
		n, ok := in.Get(fetch)
		if !ok {
			return "", errors.New("fetch output unavailable")
		}
		return fmt.Sprintf("stored %d", n), nil
	}, workflow.After(fetch, workflow.OnSuccess))

	wf, err := b.Build()
	if err != nil {
		fmt.Println("build:", err)
		return
	}
	exec := wf.Execute(context.Background())
	result, _ := exec.Get(store)
	fmt.Println(result)
	fmt.Println(exec.Results()["fetch"], exec.Results()["store"], exec.Err())
}
Output:
stored 42
success success <nil>
Example (OnFailure)

ExampleBuilder_onFailure runs a fallback only when the primary step fails.

package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/libtnb/cron"
	"github.com/libtnb/cron/workflow"
)

func main() {
	b := workflow.New()
	primary := b.Job("primary", cron.JobFunc(func(ctx context.Context) error {
		return errors.New("upstream down")
	}))
	b.Job("fallback", cron.JobFunc(func(ctx context.Context) error {
		fmt.Println("fallback ran")
		return nil
	}), workflow.After(primary, workflow.OnFailure))
	b.Job("report", cron.JobFunc(func(ctx context.Context) error {
		fmt.Println("report ran")
		return nil
	}), workflow.After(primary, workflow.OnSuccess))

	exec := b.MustBuild().Execute(context.Background())
	fmt.Println(exec.Results()["fallback"], exec.Results()["report"])
	fmt.Println(exec.Err())
}
Output:
fallback ran
success skipped
upstream down

func New

func New(opts ...Option) *Builder

New returns an empty Builder configured by opts. Invalid options are reported by Build, not here.

func (*Builder) Build added in v0.5.3

func (b *Builder) Build() (*Workflow, error)

Build validates the graph, freezes the Builder and returns an immutable Workflow. Repeated calls return the same result. It returns the joined option errors (wrapping ErrInvalidOption), a *ConfigError wrapping ErrNilJob, ErrInvalidName, ErrDuplicateStep, ErrUnknownDep or ErrDuplicateDep, ErrCycle with the cycle's path, or ErrBuilderFrozen when steps were added after an earlier Build.

func (*Builder) Job added in v0.5.3

func (b *Builder) Job(name string, job cron.Job, opts ...StepOption) Output[Unit]

Job adds a cron.Job step whose Output type is Unit. It follows the same rules as Step.

func (*Builder) MustBuild added in v0.5.3

func (b *Builder) MustBuild() *Workflow

MustBuild is Build that panics on error, for graphs fixed at build time.

func (*Builder) Step added in v0.5.3

func (b *Builder) Step[T any](
	name string,
	fn func(context.Context, Inputs) (T, error),
	opts ...StepOption,
) Output[T]

Step adds a step computing a T and returns its Output for use with After and Get. Steps run in dependency order, not registration order. name must be unique, non-empty and free of surrounding whitespace (Build reports ErrDuplicateStep or ErrInvalidName); a nil fn is reported as ErrNilJob. A panic in fn is recovered into a failure result.

type Condition

type Condition uint8

Condition selects which upstream Result satisfies a dependency declared with After.

const (

	// OnSuccess runs the step only if the dependency succeeded.
	OnSuccess Condition
	// OnFailure runs the step only if the dependency failed or panicked.
	OnFailure
	// OnSkipped runs the step only if the dependency was skipped.
	OnSkipped
	// OnComplete runs the step whatever the dependency's result.
	OnComplete
)

type ConfigError

type ConfigError struct {
	Err  error
	Step string
	Dep  string
}

ConfigError identifies the step, and for dependency faults the dependency, involved in an invalid graph. Err is one of ErrDuplicateStep, ErrNilJob, ErrInvalidName, ErrUnknownDep or ErrDuplicateDep.

func (*ConfigError) Error

func (e *ConfigError) Error() string

Error names the step and, for dependency faults, the dependency.

func (*ConfigError) Unwrap

func (e *ConfigError) Unwrap() error

Unwrap exposes the sentinel to errors.Is.

type Execution

type Execution struct {
	ID        uuid.UUID // time-ordered (version 7), unique per run
	StartedAt time.Time
	Duration  time.Duration
	// contains filtered or unexported fields
}

Execution reports one completed Workflow run. It is immutable and safe for concurrent reads; the accessors return copies.

func (*Execution) Err

func (e *Execution) Err() error

Err joins the step errors in registration order, or returns nil when no step failed. A step skipped because the context was cancelled contributes the cancellation cause; one skipped for an unmet condition contributes nothing. A nil ctx passed to Execute yields ErrNilContext.

func (*Execution) Error added in v0.5.3

func (e *Execution) Error(name string) error

Error returns the named step's error, or nil for success, a skip without cause, or an unknown name.

func (*Execution) Errors

func (e *Execution) Errors() map[string]error

Errors returns a copy of the non-nil step errors keyed by name.

func (*Execution) Get added in v0.5.3

func (e *Execution) Get[T any](output Output[T]) (T, bool)

Get returns the typed output of a successful step. ok is false when the step failed or was skipped, when output belongs to another graph, or for the zero Output.

func (*Execution) Result added in v0.5.3

func (e *Execution) Result(name string) (Result, bool)

Result returns the named step's Result; ok is false for an unknown name.

func (*Execution) Results

func (e *Execution) Results() map[string]Result

Results returns a copy of every step's Result keyed by name.

func (*Execution) Step added in v0.5.3

func (e *Execution) Step(name string) (StepReport, bool)

Step returns the named step's report; ok is false for an unknown name.

func (*Execution) Steps added in v0.3.0

func (e *Execution) Steps() map[string]StepReport

Steps returns a copy of every step's report keyed by name.

type Inputs added in v0.3.0

type Inputs struct {
	// contains filtered or unexported fields
}

Inputs gives a running step the outputs of its declared dependencies. Only successful dependencies carry a value: Get reports ok == false for a dependency that failed or was skipped, for a step the caller did not declare with After, and for an Output from another graph.

func (Inputs) Get added in v0.5.3

func (in Inputs) Get[T any](output Output[T]) (T, bool)

Get resolves a declared dependency's output with its exact type.

type Option added in v0.5.3

type Option func(*builderConfig) error

Option configures a Builder; see New.

func WithMaxParallelism added in v0.5.3

func WithMaxParallelism(limit int) Option

WithMaxParallelism limits how many steps run at the same time. The default is 32; a non-positive limit fails Build with ErrInvalidOption.

type Output added in v0.5.3

type Output[T any] struct {
	// contains filtered or unexported fields
}

Output identifies one step's typed output within its graph. Pass it to After to declare a dependency and to Inputs.Get or Execution.Get to read the value. The zero Output belongs to no graph and is rejected everywhere.

func (Output[T]) Name added in v0.5.3

func (o Output[T]) Name() string

Name returns the step name.

type Result

type Result uint8

Result is a step outcome as reported by Execution.

const (
	// ResultPending is a step's state before it completes; it never appears
	// in a finished Execution.
	ResultPending Result = iota
	// ResultSuccess reports that the step returned no error.
	ResultSuccess
	// ResultFailure reports that the step returned an error or panicked.
	ResultFailure
	// ResultSkipped reports that the step did not run because a dependency
	// condition was not met or the context was already cancelled.
	ResultSkipped
)

func (Result) String

func (r Result) String() string

String returns "pending", "success", "failure", "skipped" or "unknown".

type StepOption added in v0.5.3

type StepOption func(*step) error

StepOption configures one step at Builder.Step or Builder.Job time. Options are applied in order; failures are reported by Build.

func After

func After[T any](output Output[T], when Condition) StepOption

After declares that the step runs only after output's step completes with a result matching when. A step waits for all its dependencies and is skipped if any condition fails. The zero Output or an unknown Condition fails Build with ErrInvalidOption; declaring the same dependency twice fails it with ErrDuplicateDep.

func WithRetry added in v0.5.3

func WithRetry(policy cron.RetryPolicy) StepOption

WithRetry re-runs one step on error according to policy (see cron.RetryPolicy). A policy with negative delays or a jitter fraction outside [0, 1] fails Build with ErrInvalidOption.

func WithTimeout added in v0.5.3

func WithTimeout(timeout time.Duration) StepOption

WithTimeout caps one step run; the step context is cancelled with cron.ErrJobTimeout as the cause. Zero disables the timeout; a negative timeout fails Build with ErrInvalidOption.

type StepReport added in v0.3.0

type StepReport struct {
	Result    Result
	Err       error         // step error, or the cancellation cause for a step skipped by a cancelled context
	StartedAt time.Time     // zero for skipped steps
	Duration  time.Duration // zero for skipped steps
	// contains filtered or unexported fields
}

StepReport is one step's outcome within an Execution.

type Unit added in v0.5.3

type Unit struct{}

Unit is the output type of steps added with Builder.Job; it carries no data.

type Workflow

type Workflow struct {
	// contains filtered or unexported fields
}

Workflow is an immutable DAG produced by Builder.Build. It implements cron.Job, so it can be registered with Cron.Add or Cron.AddSchedule, and is safe for concurrent use: every Run or Execute is an independent execution.

func (*Workflow) Execute added in v0.5.3

func (w *Workflow) Execute(ctx context.Context) *Execution

Execute runs the DAG once and blocks until every step has completed or been skipped; at most the configured number of steps run at the same time. A step is skipped when a dependency condition is not met or when ctx is already cancelled by the time the step becomes ready; steps already running observe the cancellation through their context. A nil ctx skips every step and reports ErrNilContext.

func (*Workflow) Run

func (w *Workflow) Run(ctx context.Context) error

Run executes the DAG once and returns Execution.Err, which makes a Workflow a cron.Job.

func (*Workflow) WithOnComplete

func (w *Workflow) WithOnComplete(cb func(*Execution)) *Workflow

WithOnComplete returns a copy of w that calls cb with the finished Execution, on the calling goroutine, before Execute returns. w itself is unchanged.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL