Documentation
¶
Overview ¶
Package pipeflow executes ordinary Go functions as sequential, observable Pipelines composed from Stages and Steps.
Business values flow through function returns. Context is reserved for execution-wide shared data, while policies add bounded execution behavior such as retry, timeout, polling, rate limiting, parallel branches, managed background work, and finalization.
The v1 API is described in API_STABILITY.md. RunReport and live state types intentionally contain execution facts rather than flowing business values.
Example (ScopedBranchContext) ¶
package main
import (
"context"
"fmt"
"github.com/bbernier33/pipeflow"
)
func main() {
shared := pipeflow.NewContext()
shared.Set("prefix", "account")
branchStep := func(label string) *pipeflow.Step {
return pipeflow.NewStep("scope", func(_ context.Context, branchCtx *pipeflow.Context, input any) (any, error) {
prefix, _ := branchCtx.Get("prefix")
branchCtx.Set("local", label)
return fmt.Sprintf("%s-%s", prefix, label), nil
})
}
parallel := pipeflow.NewParallel("providers", []pipeflow.Branch{
pipeflow.NewBranch("gmail", branchStep("gmail")),
pipeflow.NewBranch("microsoft", branchStep("microsoft")),
})
output, err := parallel.Run(context.Background(), shared, nil)
results := output.(pipeflow.ParallelResults)
_, merged := shared.Get("local")
fmt.Println(results[0].Value, results[1].Value, merged, err)
}
Output: account-gmail account-microsoft false <nil>
Example (V1APIContract) ¶
package main
import (
"context"
"errors"
"time"
pipeflow "github.com/bbernier33/pipeflow"
)
// Compile-time assignments intentionally pin the v1 method signatures used by
// callers outside the pipeflow package.
var (
_ func(string, ...pipeflow.Stage) pipeflow.Pipeline = pipeflow.NewPipeline
_ func(string, ...pipeflow.StageItem) pipeflow.Stage = pipeflow.NewStage
_ func(string, any, ...pipeflow.StepOption) *pipeflow.Step = pipeflow.NewStep
_ func([]*pipeflow.Step, ...pipeflow.ConcurrentStepsOption) *pipeflow.ConcurrentSteps = pipeflow.NewConcurrentSteps
_ func(string, ...*pipeflow.Step) pipeflow.Branch = pipeflow.NewBranch
_ func(string, []pipeflow.Branch, ...pipeflow.ParallelOption) *pipeflow.Parallel = pipeflow.NewParallel
_ func(string, ...pipeflow.Stage) *pipeflow.Subflow = pipeflow.NewSubflow
_ func(*pipeflow.Pipeline, context.Context, ...any) (any, error) = (*pipeflow.Pipeline).Run
_ func(*pipeflow.Pipeline, context.Context, ...any) (any, pipeflow.RunReport, error) = (*pipeflow.Pipeline).RunWithReport
_ func(*pipeflow.Pipeline, context.Context, ...any) (*pipeflow.Execution, error) = (*pipeflow.Pipeline).Start
_ func(*pipeflow.Pipeline) error = (*pipeflow.Pipeline).Validate
_ func(*pipeflow.Pipeline, any) error = (*pipeflow.Pipeline).ValidateInput
_ func(pipeflow.Pipeline) pipeflow.Description = pipeflow.Pipeline.Describe
_ func(*pipeflow.Execution) pipeflow.Status = (*pipeflow.Execution).Status
_ func(*pipeflow.Execution) pipeflow.CurrentExecution = (*pipeflow.Execution).Current
_ func(*pipeflow.Execution) pipeflow.ExecutionState = (*pipeflow.Execution).State
_ func(*pipeflow.Execution) pipeflow.RunReport = (*pipeflow.Execution).Report
_ func(*pipeflow.Execution) <-chan struct{} = (*pipeflow.Execution).Done
_ func(*pipeflow.Execution) (any, pipeflow.RunReport, error) = (*pipeflow.Execution).Wait
_ pipeflow.StageItem = (*pipeflow.Step)(nil)
_ pipeflow.StageItem = (*pipeflow.ConcurrentSteps)(nil)
_ pipeflow.StageItem = (*pipeflow.Parallel)(nil)
_ pipeflow.StageItem = (*pipeflow.Subflow)(nil)
_ pipeflow.Logger = pipeflow.DefaultLogger{}
_ pipeflow.RetryAfterError = retryAfterContractError{}
)
type retryAfterContractError struct{}
func (retryAfterContractError) Error() string { return "retry" }
func (retryAfterContractError) RetryAfter() time.Duration { return 0 }
func main() {
pipeline := pipeflow.NewPipeline("orders", pipeflow.NewStage("process",
pipeflow.NewStep("load", func() (int, error) { return 21, nil }),
pipeflow.NewStep("double", func(value int) (int, error) { return value * 2, nil },
pipeflow.WithRetry(pipeflow.RetryPolicy{MaxAttempts: 3}),
pipeflow.WithTimeout(time.Second)),
pipeflow.NewStep("store", func(value int) error { return nil }),
)).WithLifecycleHook(func(pipeflow.LifecycleEvent) {})
output, report, err := pipeline.RunWithReport(context.Background())
_, _, _ = output, report, err
var executionErr *pipeflow.ExecutionError
_ = errors.As(err, &executionErr)
}
Output:
Example (WithPolling) ¶
package main
import (
"context"
"fmt"
"github.com/bbernier33/pipeflow"
)
func main() {
polls := 0
pipeline := pipeflow.NewPipeline("example",
pipeflow.NewStage("wait",
pipeflow.NewStep("ready", func() (int, error) {
polls++
return polls, nil
}, pipeflow.WithPolling(pipeflow.PollPolicy{
MaxPolls: 3,
Until: func(value int) bool { return value == 2 },
})),
),
)
output, err := pipeline.Run(context.Background())
fmt.Println(output, polls, err)
}
Output: 2 2 <nil>
Example (WithRetryBackoff) ¶
package main
import (
"context"
"errors"
"fmt"
"time"
"github.com/bbernier33/pipeflow"
)
func main() {
temporary := errors.New("temporarily unavailable")
attempts := 0
pipeline := pipeflow.NewPipeline("example",
pipeflow.NewStage("fetch",
pipeflow.NewStep("users", func() (int, error) {
attempts++
if attempts < 3 {
return 0, temporary
}
return 42, nil
}, pipeflow.WithRetry(pipeflow.RetryPolicy{
MaxAttempts: 3,
Delay: time.Millisecond,
Backoff: pipeflow.ExponentialBackoff,
MaxDelay: 10 * time.Millisecond,
RetryIf: func(err error) bool { return errors.Is(err, temporary) },
})),
),
)
output, err := pipeline.Run(context.Background())
fmt.Println(output, attempts, err)
}
Output: 42 3 <nil>
Example (WithTimeout) ¶
package main
import (
"context"
"fmt"
"time"
"github.com/bbernier33/pipeflow"
)
func main() {
pipeline := pipeflow.NewPipeline("example",
pipeflow.NewStage("work",
pipeflow.NewStep("produce", func() (int, error) { return 42, nil }, pipeflow.WithTimeout(time.Second)),
).WithTimeout(2*time.Second),
).WithTimeout(3 * time.Second)
output, err := pipeline.Run(context.Background())
fmt.Println(output, err)
}
Output: 42 <nil>
Index ¶
- Variables
- type AttemptReport
- type BackgroundError
- type BackgroundFailurePolicy
- type BackgroundFunc
- type BackgroundOption
- type BackgroundReport
- type BackgroundState
- type BackoffStrategy
- type Branch
- type BranchReport
- type BranchResult
- type BranchState
- type CleanupError
- type CleanupReport
- type ConcurrentSteps
- type ConcurrentStepsOption
- type Context
- func (c *Context) CurrentStage() string
- func (c *Context) CurrentStep() string
- func (c *Context) Duration() time.Duration
- func (c *Context) EndedAt() time.Time
- func (c *Context) Get(key string) (any, bool)
- func (c *Context) Logger() Logger
- func (c *Context) Set(key string, value any)
- func (c *Context) StartedAt() time.Time
- func (c *Context) Status() Status
- type CurrentBackground
- type CurrentBranch
- type CurrentExecution
- type CurrentParallel
- type CurrentStage
- type CurrentStep
- type CurrentSubflow
- type DefaultLogger
- type Description
- type DescriptionKind
- type Execution
- type ExecutionError
- type ExecutionState
- type FailurePolicy
- type Finalization
- type Finalizer
- type LifecycleEvent
- type LifecycleEventType
- type LifecycleHook
- type Logger
- type PanicError
- type Parallel
- type ParallelOption
- type ParallelReport
- type ParallelResults
- type ParallelState
- type Pipeline
- func (p Pipeline) Describe() Description
- func (p Pipeline) Finally(name string, finalizer Finalizer) Pipeline
- func (p *Pipeline) OnCompleted(hook PipelineHook)
- func (p *Pipeline) OnFailed(hook PipelineHook)
- func (p *Pipeline) OnStarted(hook PipelineHook)
- func (p *Pipeline) Run(goCtx context.Context, args ...any) (any, error)
- func (p *Pipeline) RunWithReport(goCtx context.Context, args ...any) (any, RunReport, error)
- func (p *Pipeline) Start(goCtx context.Context, args ...any) (*Execution, error)
- func (p *Pipeline) Validate() error
- func (p *Pipeline) ValidateInput(input any) error
- func (p Pipeline) WithBackground(name string, work BackgroundFunc, options ...BackgroundOption) Pipeline
- func (p Pipeline) WithLifecycleHook(hook LifecycleHook) Pipeline
- func (p Pipeline) WithTimeout(timeout time.Duration) Pipeline
- type PipelineEvent
- type PipelineHook
- type PollPolicy
- type PollReport
- type RateLimitPolicy
- type ResultMetadata
- type RetryAfterError
- type RetryPolicy
- type RunReport
- type Stage
- type StageItem
- type StageReport
- type StageState
- type Status
- type Step
- type StepOption
- type StepReport
- type StepState
- type Subflow
- type SubflowReport
- type SubflowState
Examples ¶
- Package (ScopedBranchContext)
- Package (V1APIContract)
- Package (WithPolling)
- Package (WithRetryBackoff)
- Package (WithTimeout)
- Execution.State
- ExecutionError
- NewParallel
- NewSubflow
- Pipeline.Describe
- Pipeline.Finally
- Pipeline.Run (ValueFlow)
- Pipeline.RunWithReport
- Pipeline.Start
- Pipeline.ValidateInput
- Pipeline.WithBackground
- Pipeline.WithLifecycleHook
- WithCondition
- WithRateLimit
- WithResultMetadata
Constants ¶
This section is empty.
Variables ¶
var ErrContextInUse = errors.New("pipeflow: context is already in use by another execution")
ErrContextInUse is returned when one Pipeflow Context is used by overlapping runs.
var ErrPollLimitExceeded = errors.New("pipeflow: poll limit exceeded")
ErrPollLimitExceeded is returned when polling does not satisfy its predicate within MaxPolls.
Functions ¶
This section is empty.
Types ¶
type AttemptReport ¶ added in v1.0.0
type BackgroundError ¶ added in v1.0.0
BackgroundError identifies a failed fatal background task.
func (*BackgroundError) Error ¶ added in v1.0.0
func (e *BackgroundError) Error() string
func (*BackgroundError) Unwrap ¶ added in v1.0.0
func (e *BackgroundError) Unwrap() error
type BackgroundFailurePolicy ¶ added in v1.0.0
type BackgroundFailurePolicy int
const ( BackgroundFatal BackgroundFailurePolicy = iota BackgroundNonFatal )
type BackgroundFunc ¶ added in v1.0.0
type BackgroundOption ¶ added in v1.0.0
type BackgroundOption func(*backgroundTask)
func WithBackgroundFailurePolicy ¶ added in v1.0.0
func WithBackgroundFailurePolicy(policy BackgroundFailurePolicy) BackgroundOption
type BackgroundReport ¶ added in v1.0.0
type BackgroundState ¶ added in v1.0.0
type BackgroundState struct {
Name string
FailurePolicy BackgroundFailurePolicy
Status Status
Duration time.Duration
}
type BackoffStrategy ¶ added in v1.0.0
type BackoffStrategy int
const ( FixedBackoff BackoffStrategy = iota ExponentialBackoff )
type Branch ¶ added in v1.0.0
type Branch struct {
// contains filtered or unexported fields
}
Branch is a sequential value-flow path within a Parallel group.
type BranchReport ¶ added in v1.0.0
type BranchResult ¶ added in v1.0.0
BranchResult is one successful branch's final value.
type BranchState ¶ added in v1.0.0
type CleanupError ¶ added in v1.0.0
CleanupError identifies a failed Pipeline finalizer.
func (*CleanupError) Error ¶ added in v1.0.0
func (e *CleanupError) Error() string
func (*CleanupError) Unwrap ¶ added in v1.0.0
func (e *CleanupError) Unwrap() error
type CleanupReport ¶ added in v1.0.0
type ConcurrentSteps ¶ added in v0.4.0
type ConcurrentSteps struct {
// contains filtered or unexported fields
}
func NewConcurrentSteps ¶ added in v0.4.0
func NewConcurrentSteps(steps []*Step, options ...ConcurrentStepsOption) *ConcurrentSteps
type ConcurrentStepsOption ¶ added in v0.7.0
type ConcurrentStepsOption func(*ConcurrentSteps)
func WithFailurePolicy ¶ added in v0.7.0
func WithFailurePolicy(policy FailurePolicy) ConcurrentStepsOption
func WithMaxWorkers ¶ added in v0.8.0
func WithMaxWorkers(max int) ConcurrentStepsOption
type Context ¶
type Context struct {
// contains filtered or unexported fields
}
func NewContext ¶
func NewContext() *Context
func NewContextWithLogger ¶ added in v0.2.0
func (*Context) CurrentStage ¶ added in v0.2.0
CurrentStage is retained for compatibility. Use Execution.Current for structured live execution state. Deprecated: use Execution.Current.
func (*Context) CurrentStep ¶ added in v0.2.0
CurrentStep is retained for compatibility but cannot represent concurrent Steps. Use Execution.Current for structured live execution state. Deprecated: use Execution.Current.
func (*Context) Duration ¶ added in v0.2.0
Duration returns compatibility execution timing stored on Context. Deprecated: use Execution.State, Execution.Report, or RunReport.
func (*Context) EndedAt ¶ added in v0.2.0
EndedAt returns compatibility execution timing stored on Context. Deprecated: use Execution.Report or RunReport.
type CurrentBackground ¶ added in v1.0.0
type CurrentBackground struct {
Name string
FailurePolicy BackgroundFailurePolicy
Status Status
Duration time.Duration
}
type CurrentBranch ¶ added in v1.0.0
type CurrentBranch struct {
Name string
Status Status
Duration time.Duration
Steps []CurrentStep
}
type CurrentExecution ¶ added in v1.0.0
type CurrentExecution struct {
Pipeline string
Status Status
Duration time.Duration
Stages []CurrentStage
Backgrounds []CurrentBackground
}
CurrentExecution describes only the execution units active at snapshot time.
type CurrentParallel ¶ added in v1.0.0
type CurrentParallel struct {
Name string
Status Status
Duration time.Duration
Branches []CurrentBranch
}
type CurrentStage ¶ added in v1.0.0
type CurrentStage struct {
Name string
Status Status
Duration time.Duration
Steps []CurrentStep
Parallels []CurrentParallel
Subflows []CurrentSubflow
}
type CurrentStep ¶ added in v1.0.0
type CurrentSubflow ¶ added in v1.0.0
type CurrentSubflow struct {
Name string
Status Status
Duration time.Duration
Stages []CurrentStage
}
type DefaultLogger ¶ added in v0.2.0
type DefaultLogger struct{}
func (DefaultLogger) Error ¶ added in v0.2.0
func (l DefaultLogger) Error(message string)
func (DefaultLogger) Info ¶ added in v0.2.0
func (l DefaultLogger) Info(message string)
type Description ¶ added in v1.0.0
type Description struct {
Kind DescriptionKind
Name string
Type string
Children []Description
}
Description is a definition-only Pipeline tree. It contains no functions, flowing values, result metadata, reports, or mutable execution state.
func (Description) String ¶ added in v1.0.0
func (d Description) String() string
String renders the definition as a compact Unicode tree.
type DescriptionKind ¶ added in v1.0.0
type DescriptionKind string
DescriptionKind identifies one definition node in a Pipeline description.
const ( DescriptionPipeline DescriptionKind = "pipeline" DescriptionStage DescriptionKind = "stage" DescriptionStep DescriptionKind = "step" DescriptionConcurrent DescriptionKind = "concurrent_steps" DescriptionParallel DescriptionKind = "parallel" DescriptionBranch DescriptionKind = "branch" DescriptionSubflow DescriptionKind = "subflow" DescriptionCustomItem DescriptionKind = "custom_item" DescriptionInvalidItem DescriptionKind = "invalid_item" )
type Execution ¶ added in v1.0.0
type Execution struct {
// contains filtered or unexported fields
}
Execution is a read-only, run-scoped handle for an asynchronously running Pipeline.
func (*Execution) Current ¶ added in v1.0.0
func (e *Execution) Current() CurrentExecution
Current returns the currently active stages and steps.
func (*Execution) Done ¶ added in v1.0.0
func (e *Execution) Done() <-chan struct{}
Done closes when pipeline execution finishes.
func (*Execution) Report ¶ added in v1.0.0
Report returns an immutable snapshot of the run's current report.
func (*Execution) State ¶ added in v1.0.0
func (e *Execution) State() ExecutionState
State returns a lightweight snapshot of every execution unit. Unlike Current, it includes pending and completed siblings as well as active work.
Example ¶
package main
import (
"context"
"fmt"
"github.com/bbernier33/pipeflow"
)
func main() {
started := make(chan struct{})
release := make(chan struct{})
pipeline := pipeflow.NewPipeline("example",
pipeflow.NewStage("prepare", pipeflow.NewStep("load", func() error { return nil })),
pipeflow.NewStage("process", pipeflow.NewStep("wait", func() error {
close(started)
<-release
return nil
})),
pipeflow.NewStage("output", pipeflow.NewStep("save", func() error { return nil })),
)
execution, _ := pipeline.Start(context.Background())
<-started
state := execution.State()
for _, stage := range state.Stages {
fmt.Println(stage.Name, stage.Status)
}
close(release)
_, _, _ = execution.Wait()
}
Output: prepare completed process running output pending
type ExecutionError ¶ added in v1.0.0
type ExecutionError struct {
Pipeline string
Stage string
Step string
Parallel string
Branch string
Subflow string
Poll int
Attempt int
Err error
}
ExecutionError describes where an error occurred during Pipeflow execution. Err remains available through errors.Is, errors.As, and errors.Unwrap.
Example ¶
package main
import (
"context"
"errors"
"fmt"
"github.com/bbernier33/pipeflow"
)
func main() {
cause := errors.New("database unavailable")
pipeline := pipeflow.NewPipeline("orders",
pipeflow.NewStage("output",
pipeflow.NewStep("save", func() error { return cause }),
),
)
_, err := pipeline.Run(context.Background())
var executionErr *pipeflow.ExecutionError
if errors.As(err, &executionErr) {
fmt.Println(executionErr.Pipeline, executionErr.Stage, executionErr.Step, executionErr.Attempt)
}
fmt.Println(errors.Is(err, cause))
}
Output: orders output save 1 true
func (*ExecutionError) Error ¶ added in v1.0.0
func (e *ExecutionError) Error() string
func (*ExecutionError) Unwrap ¶ added in v1.0.0
func (e *ExecutionError) Unwrap() error
type ExecutionState ¶ added in v1.0.0
type ExecutionState struct {
RunID string
Pipeline string
Status Status
Duration time.Duration
Stages []StageState
Backgrounds []BackgroundState
}
ExecutionState is a read-only, payload-free snapshot of the complete run topology, including pending, active, and completed execution units.
type FailurePolicy ¶ added in v0.7.0
type FailurePolicy int
const ( WaitAll FailurePolicy = iota FailFast )
type Finalization ¶ added in v1.0.0
Finalization describes the execution outcome before cleanup errors are added.
type Finalizer ¶ added in v1.0.0
type Finalizer func(context.Context, Finalization) error
Finalizer releases resources after Pipeline work has stopped.
type LifecycleEvent ¶ added in v1.0.0
type LifecycleEvent struct {
Type LifecycleEventType
RunID string
Pipeline string
Stage string
Step string
Parallel string
Branch string
Background string
Subflow string
Status Status
OccurredAt time.Time
Err error
}
LifecycleEvent is an immutable execution fact. It never contains flowing business values and does not provide execution control.
type LifecycleEventType ¶ added in v1.0.0
type LifecycleEventType string
LifecycleEventType identifies a stable execution lifecycle boundary.
const ( PipelineStarted LifecycleEventType = "pipeline_started" PipelineCompleted LifecycleEventType = "pipeline_completed" PipelineFailed LifecycleEventType = "pipeline_failed" PipelineFinalized LifecycleEventType = "pipeline_finalized" StageStarted LifecycleEventType = "stage_started" StageCompleted LifecycleEventType = "stage_completed" StageFailed LifecycleEventType = "stage_failed" StepStarted LifecycleEventType = "step_started" StepCompleted LifecycleEventType = "step_completed" StepFailed LifecycleEventType = "step_failed" StepSkipped LifecycleEventType = "step_skipped" SubflowStarted LifecycleEventType = "subflow_started" SubflowCompleted LifecycleEventType = "subflow_completed" SubflowFailed LifecycleEventType = "subflow_failed" ParallelStarted LifecycleEventType = "parallel_started" ParallelCompleted LifecycleEventType = "parallel_completed" ParallelFailed LifecycleEventType = "parallel_failed" BranchStarted LifecycleEventType = "branch_started" BranchCompleted LifecycleEventType = "branch_completed" BranchFailed LifecycleEventType = "branch_failed" BackgroundStarted LifecycleEventType = "background_started" BackgroundCompleted LifecycleEventType = "background_completed" BackgroundFailed LifecycleEventType = "background_failed" )
type LifecycleHook ¶ added in v1.0.0
type LifecycleHook func(LifecycleEvent)
LifecycleHook observes execution synchronously. Hooks cannot mutate flowing values; a hook panic fails its owning boundary. Callers should keep hooks brief.
type PanicError ¶ added in v1.0.0
PanicError represents a panic recovered from user execution or cleanup code.
func (*PanicError) Error ¶ added in v1.0.0
func (e *PanicError) Error() string
type Parallel ¶ added in v1.0.0
type Parallel struct {
// contains filtered or unexported fields
}
Parallel executes isolated Branches concurrently and joins their outputs in declaration order.
func NewParallel ¶ added in v1.0.0
func NewParallel(name string, branches []Branch, options ...ParallelOption) *Parallel
NewParallel creates a named group of concurrent Branches.
Example ¶
package main
import (
"context"
"fmt"
"github.com/bbernier33/pipeflow"
)
func main() {
pipeline := pipeflow.NewPipeline("providers", pipeflow.NewStage("lookup",
pipeflow.NewParallel("accounts", []pipeflow.Branch{
pipeflow.NewBranch("gmail", pipeflow.NewStep("lookup", func(id int) (string, error) {
return fmt.Sprintf("gmail-%d", id), nil
})),
pipeflow.NewBranch("microsoft", pipeflow.NewStep("lookup", func(id int) (string, error) {
return fmt.Sprintf("microsoft-%d", id), nil
})),
}),
pipeflow.NewStep("join", func(results pipeflow.ParallelResults) error {
fmt.Println(results[0].Name, results[0].Value)
fmt.Println(results[1].Name, results[1].Value)
return nil
}),
))
_, err := pipeline.Run(context.Background(), 7)
fmt.Println(err)
}
Output: gmail gmail-7 microsoft microsoft-7 <nil>
type ParallelOption ¶ added in v1.0.0
type ParallelOption func(*Parallel)
ParallelOption configures a Parallel group.
func WithParallelFailurePolicy ¶ added in v1.0.0
func WithParallelFailurePolicy(policy FailurePolicy) ParallelOption
WithParallelFailurePolicy selects WaitAll or FailFast behavior.
type ParallelReport ¶ added in v1.0.0
type ParallelResults ¶ added in v1.0.0
type ParallelResults []BranchResult
ParallelResults contains successful branch outputs in declaration order.
type ParallelState ¶ added in v1.0.0
type ParallelState struct {
Name string
Status Status
Duration time.Duration
Branches []BranchState
}
type Pipeline ¶
type Pipeline struct {
// contains filtered or unexported fields
}
func NewPipeline ¶
NewPipeline creates a sequential Pipeline from stages.
func (Pipeline) Describe ¶ added in v1.0.0
func (p Pipeline) Describe() Description
Describe returns a fresh structural description of the Pipeline.
Example ¶
package main
import (
"fmt"
"github.com/bbernier33/pipeflow"
)
func main() {
pipeline := pipeflow.NewPipeline("Invoice Processing",
pipeflow.NewStage("Ingest",
pipeflow.NewStep("Read", func() error { return nil }),
pipeflow.NewStep("Parse", func() error { return nil }),
),
pipeflow.NewStage("Output",
pipeflow.NewStep("Store", func() error { return nil }),
),
)
fmt.Println(pipeline.Describe())
}
Output: Invoice Processing ├── Ingest │ ├── Read │ └── Parse └── Output └── Store
func (Pipeline) Finally ¶ added in v1.0.0
Finally returns a Pipeline with a named cleanup callback. Finalizers run in reverse registration order after execution work stops.
Example ¶
package main
import (
"context"
"fmt"
"github.com/bbernier33/pipeflow"
)
func main() {
pipeline := pipeflow.NewPipeline("temporary-worker",
pipeflow.NewStage("work", pipeflow.NewStep("process", func() error { return nil })),
).Finally("stop worker", func(ctx context.Context, result pipeflow.Finalization) error {
fmt.Println("cleanup", result.Status, ctx.Err())
return nil
})
_, report, err := pipeline.RunWithReport(context.Background())
fmt.Println(report.Status, report.Cleanups[0].Name, err)
}
Output: cleanup completed <nil> completed stop worker <nil>
func (*Pipeline) OnCompleted ¶ added in v0.3.0
func (p *Pipeline) OnCompleted(hook PipelineHook)
OnCompleted registers the original Pipeline-completed callback. Deprecated: use WithLifecycleHook and observe PipelineCompleted.
func (*Pipeline) OnFailed ¶ added in v0.3.0
func (p *Pipeline) OnFailed(hook PipelineHook)
OnFailed registers the original Pipeline-failed callback. Deprecated: use WithLifecycleHook and observe PipelineFailed.
func (*Pipeline) OnStarted ¶ added in v0.3.0
func (p *Pipeline) OnStarted(hook PipelineHook)
OnStarted registers the original Pipeline-start callback. Deprecated: use WithLifecycleHook and observe PipelineStarted.
func (*Pipeline) Run ¶
Run executes the Pipeline. It accepts an optional input, or the compatibility form (*Context, input), and returns the final flowing value.
Example (ValueFlow) ¶
package main
import (
"context"
"fmt"
"github.com/bbernier33/pipeflow"
)
func main() {
pipeline := pipeflow.NewPipeline("orders",
pipeflow.NewStage("prepare",
pipeflow.NewStep("load", func() (int, error) { return 20, nil }),
pipeflow.NewStep("audit", func(total int) error {
fmt.Println("audited", total)
return nil
}),
pipeflow.NewStep("tax", func(total int) (int, error) { return total + 2, nil }),
),
pipeflow.NewStage("send",
pipeflow.NewStep("email", func(total int) error {
fmt.Println("emailed", total)
return nil
}),
),
)
output, err := pipeline.Run(context.Background())
if err != nil {
panic(err)
}
fmt.Println("result", output)
}
Output: audited 20 emailed 22 result 22
func (*Pipeline) RunWithReport ¶ added in v1.0.0
RunWithReport executes the pipeline and returns its structured execution report.
Example ¶
package main
import (
"context"
"fmt"
"github.com/bbernier33/pipeflow"
)
func main() {
pipeline := pipeflow.NewPipeline("example",
pipeflow.NewStage("work", pipeflow.NewStep("produce", func() (int, error) { return 42, nil })),
)
output, report, err := pipeline.RunWithReport(context.Background())
if err != nil {
panic(err)
}
fmt.Println(output, report.Status, report.Stages[0].Steps[0].Status)
}
Output: 42 completed completed
func (*Pipeline) Start ¶ added in v1.0.0
Start begins pipeline execution asynchronously and returns run-scoped state.
Example ¶
package main
import (
"context"
"fmt"
"github.com/bbernier33/pipeflow"
)
func main() {
pipeline := pipeflow.NewPipeline("example",
pipeflow.NewStage("work", pipeflow.NewStep("produce", func() (int, error) { return 42, nil })),
)
execution, err := pipeline.Start(context.Background())
if err != nil {
panic(err)
}
output, report, err := execution.Wait()
if err != nil {
panic(err)
}
fmt.Println(output, report.Status)
}
Output: 42 completed
func (*Pipeline) Validate ¶ added in v1.0.0
Validate checks statically knowable value-flow compatibility across the pipeline.
func (*Pipeline) ValidateInput ¶ added in v1.0.0
ValidateInput validates the Pipeline structure and value flow using an actual initial input. Run and Start call it before execution begins.
Example ¶
package main
import (
"fmt"
"github.com/bbernier33/pipeflow"
)
func main() {
pipeline := pipeflow.NewPipeline("orders", pipeflow.NewStage("process",
pipeflow.NewStep("validate", func(orderID int) error { return nil }),
))
fmt.Println(pipeline.Validate())
fmt.Println(pipeline.ValidateInput(42))
fmt.Println(pipeline.ValidateInput("wrong") != nil)
}
Output: <nil> <nil> true
func (Pipeline) WithBackground ¶ added in v1.0.0
func (p Pipeline) WithBackground(name string, work BackgroundFunc, options ...BackgroundOption) Pipeline
WithBackground returns a Pipeline with managed support work. Background work is cancelled and joined before finalizers run.
Example ¶
package main
import (
"context"
"fmt"
"github.com/bbernier33/pipeflow"
)
func main() {
pipeline := pipeflow.NewPipeline("service",
pipeflow.NewStage("work", pipeflow.NewStep("produce", func() (int, error) { return 42, nil })),
).WithBackground("heartbeat", func(ctx context.Context) error {
<-ctx.Done()
return ctx.Err()
})
output, report, err := pipeline.RunWithReport(context.Background())
fmt.Println(output, report.Backgrounds[0].Name, report.Backgrounds[0].Status, err)
}
Output: 42 heartbeat completed <nil>
func (Pipeline) WithLifecycleHook ¶ added in v1.0.0
func (p Pipeline) WithLifecycleHook(hook LifecycleHook) Pipeline
WithLifecycleHook returns a Pipeline observed by hook. Hooks run synchronously in registration order. A hook panic fails its owning boundary.
Example ¶
package main
import (
"context"
"fmt"
"github.com/bbernier33/pipeflow"
)
func main() {
pipeline := pipeflow.NewPipeline("orders",
pipeflow.NewStage("process", pipeflow.NewStep("charge", func() error { return nil })),
).WithLifecycleHook(func(event pipeflow.LifecycleEvent) {
if event.Type == pipeflow.StepCompleted {
fmt.Println(event.Pipeline, event.Stage, event.Step, event.Status)
}
})
_, err := pipeline.Run(context.Background())
fmt.Println(err)
}
Output: orders process charge completed <nil>
type PipelineEvent ¶ added in v0.3.0
PipelineEvent is the compatibility event used by the original Pipeline-only hooks. Deprecated: use LifecycleEvent with Pipeline lifecycle event types.
type PipelineHook ¶ added in v0.3.0
type PipelineHook func(PipelineEvent)
PipelineHook is the compatibility callback used by OnStarted, OnCompleted, and OnFailed. Deprecated: use LifecycleHook.
type PollPolicy ¶ added in v1.0.0
PollPolicy controls successful-operation polling for a Step.
type PollReport ¶ added in v1.0.0
type RateLimitPolicy ¶ added in v1.0.0
RateLimitPolicy controls Step invocation rate and concurrency within one run. Steps with the same non-empty Key share a limiter.
type ResultMetadata ¶ added in v1.0.0
ResultMetadata contains small scalar facts about successful execution. It is copied into reports and must not contain business payloads.
type RetryAfterError ¶ added in v1.0.0
RetryAfterError is implemented by errors that specify a minimum delay before another invocation. Pipeflow discovers it through errors.As.
type RetryPolicy ¶ added in v0.5.0
type RunReport ¶ added in v1.0.0
type RunReport struct {
RunID string
Pipeline string
Status Status
StartedAt time.Time
EndedAt time.Time
Duration time.Duration
Stages []StageReport
Cleanups []CleanupReport
Backgrounds []BackgroundReport
Error error
}
RunReport contains execution facts and never retains flowing business values.
type Stage ¶
type Stage struct {
// contains filtered or unexported fields
}
Stage is a sequential group of StageItems whose final value becomes the input to the next Stage.
func (*Stage) Validate ¶ added in v1.0.0
Validate checks statically knowable value-flow compatibility between steps.
func (*Stage) ValidateInput ¶ added in v1.0.0
ValidateInput validates Stage value flow using an actual initial input.
func (Stage) WithResultMetadata ¶ added in v1.0.0
WithResultMetadata returns a Stage that extracts small scalar reporting facts from its successful final value.
type StageItem ¶ added in v0.4.0
StageItem is an executable unit within a Stage. Step, ConcurrentSteps, Parallel, and Subflow are the built-in implementations.
type StageReport ¶ added in v1.0.0
type StageReport struct {
Name string
Status Status
StartedAt time.Time
EndedAt time.Time
Duration time.Duration
Steps []StepReport
Parallels []ParallelReport
Subflows []SubflowReport
Error error
Metadata ResultMetadata
}
type StageState ¶ added in v1.0.0
type StageState struct {
Name string
Status Status
Duration time.Duration
Steps []StepState
Parallels []ParallelState
Subflows []SubflowState
}
type Step ¶
type Step struct {
// contains filtered or unexported fields
}
Step adapts one supported ordinary Go function into a value-flow execution unit.
type StepOption ¶ added in v0.5.0
type StepOption func(*Step)
func WithCondition ¶ added in v1.0.0
func WithCondition(condition any) StepOption
WithCondition runs a Step only when its predicate returns true. A skipped Step preserves its input value.
Example ¶
package main
import (
"context"
"fmt"
"github.com/bbernier33/pipeflow"
)
func main() {
pipeline := pipeflow.NewPipeline("orders", pipeflow.NewStage("process",
pipeflow.NewStep("load", func() (int, error) { return 20, nil }),
pipeflow.NewStep("discount", func(total int) (int, error) { return total - 5, nil },
pipeflow.WithCondition(func(total int) bool { return total >= 100 })),
pipeflow.NewStep("send", func(total int) error {
fmt.Println(total)
return nil
}),
))
output, err := pipeline.Run(context.Background())
fmt.Println(output, err)
}
Output: 20 20 <nil>
func WithPolling ¶ added in v1.0.0
func WithPolling(policy PollPolicy) StepOption
WithPolling repeats a successful Step operation until its predicate is true.
func WithRateLimit ¶ added in v1.0.0
func WithRateLimit(policy RateLimitPolicy) StepOption
WithRateLimit limits Step invocations within a Pipeline run. The policy is applied to every retry and polling invocation.
Example ¶
package main
import (
"context"
"fmt"
"time"
"github.com/bbernier33/pipeflow"
)
func main() {
policy := pipeflow.RateLimitPolicy{
Key: "provider-api",
MaxCalls: 10,
Interval: time.Second,
MaxConcurrent: 2,
}
pipeline := pipeflow.NewPipeline("example", pipeflow.NewStage("call",
pipeflow.NewStep("request", func() error { return nil }, pipeflow.WithRateLimit(policy)),
))
_, err := pipeline.Run(context.Background())
fmt.Println(err)
}
Output: <nil>
func WithResultMetadata ¶ added in v1.0.0
func WithResultMetadata(extractor any) StepOption
WithResultMetadata extracts small scalar reporting facts from a successful Step result without changing the flowing value.
Example ¶
package main
import (
"context"
"fmt"
"github.com/bbernier33/pipeflow"
)
func main() {
pipeline := pipeflow.NewPipeline("example", pipeflow.NewStage("import",
pipeflow.NewStep("records", func() (int, error) { return 42, nil },
pipeflow.WithResultMetadata(func(count int) pipeflow.ResultMetadata {
return pipeflow.ResultMetadata{"records_processed": count, "source": "gmail"}
})),
))
output, report, err := pipeline.RunWithReport(context.Background())
metadata := report.Stages[0].Steps[0].Metadata
fmt.Println(output, metadata["records_processed"], metadata["source"], err)
}
Output: 42 42 gmail <nil>
func WithRetry ¶ added in v0.5.0
func WithRetry(policy RetryPolicy) StepOption
func WithTimeout ¶ added in v1.0.0
func WithTimeout(timeout time.Duration) StepOption
WithTimeout limits the total Step execution, including retries and delays.
type StepReport ¶ added in v1.0.0
type StepReport struct {
Name string
Status Status
StartedAt time.Time
EndedAt time.Time
Duration time.Duration
Attempts []AttemptReport
Polls []PollReport
Error error
Metadata ResultMetadata
}
type Subflow ¶ added in v1.0.0
type Subflow struct {
// contains filtered or unexported fields
}
Subflow is a reusable sequential group of Stages embedded in a parent run. It shares the parent's Context, cancellation, value flow, and Run ID.
func NewSubflow ¶ added in v1.0.0
NewSubflow creates a reusable sequential Stage group.
Example ¶
package main
import (
"context"
"fmt"
"strings"
"github.com/bbernier33/pipeflow"
)
func main() {
prepare := pipeflow.NewSubflow("prepare",
pipeflow.NewStage("normalize", pipeflow.NewStep("trim", func(value string) (string, error) {
return strings.TrimSpace(value), nil
})),
pipeflow.NewStage("measure", pipeflow.NewStep("length", func(value string) (int, error) {
return len(value), nil
})),
)
pipeline := pipeflow.NewPipeline("example", pipeflow.NewStage("process",
prepare,
pipeflow.NewStep("double", func(value int) (int, error) { return value * 2, nil }),
))
output, err := pipeline.Run(context.Background(), " abc ")
fmt.Println(output, err)
}
Output: 6 <nil>
func (*Subflow) Run ¶ added in v1.0.0
Run executes the Subflow directly without creating an independent Pipeline run.
func (*Subflow) Validate ¶ added in v1.0.0
Validate checks Subflow structure and statically knowable value flow.
func (*Subflow) ValidateInput ¶ added in v1.0.0
ValidateInput validates Subflow value flow using an actual initial input.
type SubflowReport ¶ added in v1.0.0
type SubflowReport struct {
Name string
Status Status
StartedAt time.Time
EndedAt time.Time
Duration time.Duration
Stages []StageReport
Error error
}
SubflowReport contains a reusable group's nested Stage reports.
type SubflowState ¶ added in v1.0.0
type SubflowState struct {
Name string
Status Status
Duration time.Duration
Stages []StageState
}
Source Files
¶
- background.go
- cleanup.go
- concurrent_steps.go
- condition.go
- context.go
- description.go
- doc.go
- execution.go
- execution_error.go
- failure_policy.go
- hooks.go
- lifecycle.go
- logger.go
- parallel.go
- pipeline.go
- polling.go
- rate_limit.go
- report.go
- result_metadata.go
- retry.go
- stage.go
- stage_item.go
- status.go
- step.go
- step_option.go
- subflow.go
- validation.go