Documentation
¶
Overview ¶
Package selvedge provides a durable execution framework for Go.
Index ¶
- Constants
- Variables
- func Await(ctx *WorkflowContext, cond func() bool) error
- func CancelWorkflow(ctx context.Context, app *App, instanceID, reason string) error
- func EmbeddedMigrationsFS() fs.FS
- func IsTerminalError(err error) bool
- func Now(ctx *WorkflowContext) (value time.Time, err error)
- func Pending(ctx *WorkflowContext, channel string) int
- func Query[R any](ctx context.Context, app *App, instanceID, name string) (R, error)
- func Receive[T any](ctx *WorkflowContext, channel string) (T, error)
- func ReceiveAsync[T any](ctx *WorkflowContext, channel string) (value T, received bool, err error)
- func ReceiveWithTimeout[T any](ctx *WorkflowContext, channel string, timeout time.Duration) (T, error)
- func RecordHeartbeat(ctx context.Context) error
- func RegisterActivity[I, O any](app *App, a *Activity[I, O]) error
- func RegisterWorkflow[I, O any](app *App, workflow Workflow[I, O])
- func ReplayHistory(ctx context.Context, app *App, history *RecordedHistory) error
- func ReplayInstance(ctx context.Context, app *App, id string) error
- func SetQueryHandler[R any](ctx *WorkflowContext, name string, handler func() (R, error)) error
- func Sleep(ctx *WorkflowContext, duration time.Duration, opts ...SleepOption) error
- func SleepUntil(ctx *WorkflowContext, t time.Time, opts ...SleepOption) error
- func StartWorkflow[I, O any](ctx context.Context, app *App, workflow Workflow[I, O], input I, ...) (string, error)
- func StartWorkflowByName(ctx context.Context, app *App, workflow string, input any, opts ...StartOption) (string, error)
- type Activity
- type ActivityInfo
- type ActivityOption
- type ActivityOptions
- type ActivityTaskInfo
- type ActivityTimeoutError
- type App
- func (a *App) FindInstances(ctx context.Context, inputFilters map[string]any) ([]*storage.WorkflowInstance, error)
- func (a *App) FindInstancesWithOptions(ctx context.Context, opts storage.ListInstancesOptions) ([]*storage.WorkflowInstance, error)
- func (a *App) GetInstance(ctx context.Context, instanceID string) (*storage.WorkflowInstance, error)
- func (a *App) Shutdown(ctx context.Context) error
- func (a *App) Signal(ctx context.Context, instanceID, channel string, payload any) error
- func (a *App) Start(ctx context.Context) error
- func (a *App) Storage() storage.Storage
- func (a *App) WorkerID() string
- type ApplicationError
- type Child
- type ChildOption
- type ExecuteOption
- type FindInstancesOptions
- type Future
- type InstanceClosedError
- type LockAcquisitionError
- type NondeterminismError
- type Option
- func WithActivityPollInterval(d time.Duration) Option
- func WithActivityQueues(queues ...string) Option
- func WithAutoMigrate(enabled bool) Option
- func WithClock(clock func() time.Time) Option
- func WithDatabase(url string) Option
- func WithHooks(h hooks.WorkflowHooks) Option
- func WithLeaderHeartbeatInterval(d time.Duration) Option
- func WithLeaderLeaseDuration(d time.Duration) Option
- func WithLease(ttl, renewInterval, margin, maxSkew time.Duration) Option
- func WithListenNotify(enabled *bool) Option
- func WithMaxConcurrentActivities(n int) Option
- func WithMaxConcurrentResumptions(n int) Option
- func WithMaxConcurrentTimers(n int) Option
- func WithMaxPollInterval(d time.Duration) Option
- func WithMaxTimersPerBatch(n int) Option
- func WithMaxWorkflowsPerBatch(n int) Option
- func WithMigrationsFS(migrationsFS fs.FS) Option
- func WithNotifyReconnectDelay(d time.Duration) Option
- func WithServiceName(name string) Option
- func WithShutdownTimeout(d time.Duration) Option
- func WithTimerCheckInterval(d time.Duration) Option
- func WithWaitPollInterval(d time.Duration) Option
- func WithWorkerID(id string) Option
- func WithWorkflowResumptionInterval(d time.Duration) Option
- type QueryFailedError
- type QueryNotFoundError
- type RecordedEvent
- type RecordedHistory
- type RecordedInstance
- type RecordedSignal
- type ReplayDecodeError
- type ReplayError
- type ReplayOutcomeError
- type RetryExhaustedError
- type SleepOption
- type StartOption
- type TerminalError
- type TimeoutKind
- type Timer
- type Workflow
- type WorkflowCancelledError
- type WorkflowContext
- func Detached(ctx *WorkflowContext) *WorkflowContext
- func NewWorkflowContext(ctx context.Context, instanceID, workerID, workflowName string) *WorkflowContext
- func NewWorkflowContextFromExecution(execCtx *replay.ExecutionContext) *WorkflowContext
- func WithCancel(ctx *WorkflowContext) (scoped *WorkflowContext, cancel func())
- func (c *WorkflowContext) App() *App
- func (c *WorkflowContext) Context() context.Context
- func (c *WorkflowContext) Done() <-chan struct{}
- func (c *WorkflowContext) Err() error
- func (c *WorkflowContext) Hooks() hooks.WorkflowHooks
- func (c *WorkflowContext) InstanceID() string
- func (c *WorkflowContext) IsReplaying() bool
- func (c *WorkflowContext) SetApp(app *App)
- func (c *WorkflowContext) Storage() WorkflowStorage
- func (c *WorkflowContext) WithContext(ctx context.Context) *WorkflowContext
- func (c *WorkflowContext) WorkerID() string
- func (c *WorkflowContext) WorkflowName() string
- type WorkflowDescription
- type WorkflowFunc
- type WorkflowNotFoundError
- type WorkflowNotRegisteredError
- type WorkflowPanicError
- type WorkflowResult
- type WorkflowStatus
- type WorkflowStorage
Examples ¶
Constants ¶
const ( StatusRunning = storage.StatusRunning StatusCompleted = storage.StatusCompleted StatusFailed = storage.StatusFailed StatusCancelled = storage.StatusCancelled )
Variables ¶
var ErrActivityNameConflict = errors.New("activity name already registered with another definition")
ErrActivityNameConflict rejects different definitions registered under one name.
var ErrAppNotStarted = errors.New("app not started")
ErrAppNotStarted means the App has no initialized storage or has shut down.
var ErrDeterminismViolation = replay.ErrDeterminismViolation
ErrDeterminismViolation indicates non-deterministic behavior during replay.
var ErrFenced = storage.ErrFenced
ErrFenced means another execution owns the instance.
var ErrHistoryKeyConflict = storage.ErrHistoryKeyConflict
ErrHistoryKeyConflict indicates duplicate claims or conflicting stored commands.
var ErrInstanceClosed = storage.ErrInstanceClosed
var ErrInstanceNotFound = storage.ErrInstanceNotFound
var ErrInvalidSignalChannel = storage.ErrInvalidSignalChannel
var ErrInvalidSignalPayload = errors.New("invalid signal payload")
ErrInvalidSignalPayload means a signal payload could not be encoded as JSON.
var ErrInvalidWorkflowState = errors.New("invalid workflow state")
ErrInvalidWorkflowState indicates an invalid workflow state transition.
var ErrLeaseLost = replay.ErrLeaseLost
ErrLeaseLost means the execution abandoned its expired local lease belief.
var ErrNotActivityContext = errors.New("not an activity context")
ErrNotActivityContext rejects heartbeats outside an activity attempt.
var ErrQueryCommand = replay.ErrQueryCommand
ErrQueryCommand matches an attempted workflow command in a query handler.
var ErrUnsupportedHistoryFormat = errors.New("unsupported history format")
ErrUnsupportedHistoryFormat rejects unknown history formats or versions.
var ErrWorkflowAlreadyCancelled = storage.ErrWorkflowAlreadyCancelled
ErrWorkflowAlreadyCancelled indicates an operation on a cancelled workflow.
var ErrWorkflowAlreadyCompleted = storage.ErrWorkflowAlreadyCompleted
ErrWorkflowAlreadyCompleted indicates an operation on a completed workflow.
var ErrWorkflowAlreadyFailed = storage.ErrWorkflowAlreadyFailed
ErrWorkflowAlreadyFailed indicates an operation on a failed workflow.
var ErrWorkflowAlreadyStarted = storage.ErrWorkflowAlreadyStarted
ErrWorkflowAlreadyStarted indicates that an instance ID already exists.
var ErrWorkflowStartRejected = errors.New("workflow start rejected")
ErrWorkflowStartRejected means no instance insert was attempted. Duplicate IDs and uncertain storage failures do not match this error.
Functions ¶
func Await ¶
func Await(ctx *WorkflowContext, cond func() bool) error
Await waits until cond is true in the delivered view. Predicates must be deterministic and side-effect free; only IsReady, Pending and local reads are allowed. A never-true predicate parks at one replay per input. Replay runs the pre-check but never reevaluates a recorded command's candidate batch.
func CancelWorkflow ¶
CancelWorkflow durably requests cancellation and wakes a running instance. Attached scopes observe it at blocking waits. Detached scopes ignore it. A request alone does not stop activity tasks. Future.Get cancels its unfinished task when it observes the request. A stored outcome wins over cancellation. WithCancel provides local cancellation. Detached supports cleanup after either form of cancellation. Closure cancels all unfinished tasks, including detached tasks. A workflow that catches cancellation can complete normally.
func EmbeddedMigrationsFS ¶
EmbeddedMigrationsFS returns the embedded schemas rooted at sqlite/ and postgresql/. internal/migrations/sql/ is the only schema source of truth.
func IsTerminalError ¶
IsTerminalError returns true if the error is or wraps a TerminalError.
func Now ¶
func Now(ctx *WorkflowContext) (value time.Time, err error)
Now returns recorded workflow time in UTC, without a monotonic reading. Each live call writes one fenced history observation; replay returns that observation. Live time never falls below earlier Now values or timer expiries observed by Sleep or Timer.Get in this execution. On error, Now returns the last observed time and the run fails even if the caller ignores the error.
func Pending ¶
func Pending(ctx *WorkflowContext, channel string) int
Pending counts delivered, unconsumed signals at the current command position. It performs no I/O and is safe inside Await predicates. Like IsReady, it must run serially on the workflow coroutine; off-coroutine use is not detected.
func Query ¶
Query answers from a consistent snapshot replay in the calling App. Each call costs one full replay, takes no lease, and writes no workflow state. The result always passes through JSON into R, including when R is any. In-memory SQLite snapshots (":memory:" with shared cache, without WAL) block writers until the snapshot read transaction ends.
func Receive ¶
func Receive[T any](ctx *WorkflowContext, channel string) (T, error)
Receive returns the next signal from this instance's channel in arrival order. Replay returns the recorded payload. Cancellation is observed before consuming a pending signal. An empty inbox yields to the dispatcher without returning.
func ReceiveAsync ¶
func ReceiveAsync[T any](ctx *WorkflowContext, channel string) (value T, received bool, err error)
ReceiveAsync records the next delivered signal, or records that none was available. It never yields, delivers new events, or observes cancellation.
func ReceiveWithTimeout ¶
func ReceiveWithTimeout[T any](ctx *WorkflowContext, channel string, timeout time.Duration) (T, error)
ReceiveWithTimeout receives the oldest signal on channel or returns an error matching context.DeadlineExceeded. A nonpositive timeout records an immediate timeout without consuming signals. Positive waits prefer cancellation, then a signal, then the timer fire, and retire a losing timer atomically.
func RecordHeartbeat ¶
RecordHeartbeat records progress for the current activity attempt without I/O. It returns the context's cancellation cause after the attempt has stopped.
func RegisterActivity ¶
RegisterActivity makes an activity available for recovered tasks on this App. Register the same definition pointer again freely; a different definition is a conflict.
func RegisterWorkflow ¶
RegisterWorkflow registers a workflow with the application. The workflow can later be started by name or by type.
func ReplayHistory ¶
func ReplayHistory(ctx context.Context, app *App, history *RecordedHistory) error
ReplayHistory checks registered code against a history without live storage or activities.
func ReplayInstance ¶
ReplayInstance exports one instance and checks it against the App's registered workflow.
func SetQueryHandler ¶
func SetQueryHandler[R any](ctx *WorkflowContext, name string, handler func() (R, error)) error
SetQueryHandler replaces an execution-local handler without issuing a command. A nil handler removes the name. Call only on the workflow coroutine.
func Sleep ¶
func Sleep(ctx *WorkflowContext, duration time.Duration, opts ...SleepOption) error
Sleep suspends the workflow for a specified duration. The workflow will resume after the duration has elapsed.
During replay, if the timer already fired, this returns immediately. Otherwise, it reserves and arms the timer under the execution fence, then yields to the dispatcher. The engine releases the lease and preserves pending input.
When the timer expires, the workflow will be resumed.
func SleepUntil ¶
func SleepUntil(ctx *WorkflowContext, t time.Time, opts ...SleepOption) error
SleepUntil suspends the workflow until a specific time.
func StartWorkflow ¶
func StartWorkflow[I, O any]( ctx context.Context, app *App, workflow Workflow[I, O], input I, opts ...StartOption, ) (string, error)
StartWorkflow starts a new workflow instance. Returns the instance ID.
func StartWorkflowByName ¶
func StartWorkflowByName(ctx context.Context, app *App, workflow string, input any, opts ...StartOption) (string, error)
StartWorkflowByName starts a workflow without requiring local registration. The returned instance ID also identifies the run; IDs cannot be reused.
Types ¶
type Activity ¶
type Activity[I, O any] struct { // contains filtered or unexported fields }
Activity represents a single unit of work within a workflow. Activities are the only way to perform I/O or side effects in workflows. I is the input type, O is the output type.
func DefineActivity ¶
func DefineActivity[I, O any]( name string, fn func(ctx context.Context, input I) (O, error), opts ...ActivityOption[I, O], ) *Activity[I, O]
DefineActivity creates a new activity.
func (*Activity[I, O]) Execute ¶
func (a *Activity[I, O]) Execute(ctx *WorkflowContext, input I, opts ...ExecuteOption) (O, error)
Execute issues the activity and waits for its durable outcome.
func (*Activity[I, O]) ExecuteAsync ¶
func (a *Activity[I, O]) ExecuteAsync(ctx *WorkflowContext, input I, opts ...ExecuteOption) (*Future[O], error)
ExecuteAsync reserves an activity in issue order without waiting for its result. Get reads the durable outcome. Un-awaited tasks are cancelled when the workflow closes.
type ActivityInfo ¶
type ActivityInfo struct {
InstanceID, WorkflowName, ActivityID, ActivityName string
Attempt int
Options ActivityOptions
}
ActivityInfo is immutable metadata for the current durable activity claim.
func GetActivityInfo ¶
func GetActivityInfo(ctx context.Context) *ActivityInfo
GetActivityInfo returns a copy of claim metadata, or nil outside an activity.
type ActivityOption ¶
ActivityOption configures an activity.
func WithActivityDefaults ¶
func WithActivityDefaults[I, O any](options ActivityOptions) ActivityOption[I, O]
WithActivityDefaults sets the definition's activity execution options.
func WithRetryPolicy ¶
func WithRetryPolicy[I, O any](policy *retry.Policy) ActivityOption[I, O]
WithRetryPolicy sets the retry policy for the activity.
func WithTimeout ¶
func WithTimeout[I, O any](d time.Duration) ActivityOption[I, O]
WithTimeout sets the timeout for each activity execution attempt.
type ActivityOptions ¶
type ActivityOptions struct {
Queue string
StartToCloseTimeout, ScheduleToStartTimeout, HeartbeatTimeout time.Duration
RetryPolicy *retry.Policy
}
ActivityOptions configures the durable execution policy recorded at reservation. Non-zero per-call fields override definition defaults. A nil policy inherits.
type ActivityTaskInfo ¶
type ActivityTaskInfo struct {
Key, Name, Queue, Status string
Seq int64
Options ActivityOptions
Attempts, Failures int
LastError error
}
ActivityTaskInfo describes the recorded policy and execution state of an activity.
func ListActivityTasks ¶
ListActivityTasks returns the instance's activities in reservation order.
type ActivityTimeoutError ¶
type ActivityTimeoutError struct {
Activity string
Kind TimeoutKind
Attempt int
}
ActivityTimeoutError describes a recorded activity deadline failure.
func (*ActivityTimeoutError) Error ¶
func (e *ActivityTimeoutError) Error() string
func (*ActivityTimeoutError) Unwrap ¶
func (e *ActivityTimeoutError) Unwrap() error
Unwrap identifies a timeout as context.DeadlineExceeded.
type App ¶
type App struct {
// contains filtered or unexported fields
}
App is the main entry point for Selvedge. It manages workflow execution, storage, and background tasks.
func (*App) FindInstances ¶
func (a *App) FindInstances(ctx context.Context, inputFilters map[string]any) ([]*storage.WorkflowInstance, error)
FindInstances searches for workflow instances with input filters. This is a convenience method for searching workflows by their input data.
The inputFilters parameter maps JSON paths to expected values:
- "customer.id" -> "12345" matches input like {"customer": {"id": "12345"}}
- "status" -> "active" matches input like {"status": "active"}
Values are compared with exact match. Supported value types:
- string: Exact string match
- int/float64: Numeric comparison
- bool: Boolean comparison
- nil: Matches null values
Example:
instances, err := app.FindInstances(ctx, map[string]any{
"order.customer_id": "cust_123",
"order.status": "pending",
})
func (*App) FindInstancesWithOptions ¶
func (a *App) FindInstancesWithOptions(ctx context.Context, opts storage.ListInstancesOptions) ([]*storage.WorkflowInstance, error)
FindInstancesWithOptions searches for workflow instances with full options. Use this when you need pagination, status filters, or other advanced options in addition to input filters.
Example ¶
This example compiles using only the public package. Register workflows and start app before calling it with stored instances.
package main
import (
"context"
"github.com/CaliLuke/selvedge"
)
func main() {
ctx := context.Background()
app := selvedge.NewApp()
instances, err := app.FindInstancesWithOptions(ctx, selvedge.FindInstancesOptions{
StatusFilter: selvedge.StatusRunning,
Limit: 100,
})
if err != nil {
return
}
for _, instance := range instances {
if err := selvedge.ReplayInstance(ctx, app, instance.InstanceID); err != nil {
return
}
}
}
Output:
func (*App) GetInstance ¶
func (a *App) GetInstance(ctx context.Context, instanceID string) (*storage.WorkflowInstance, error)
GetInstance retrieves a workflow instance by ID.
func (*App) Shutdown ¶
Shutdown gracefully shuts down the application. Cleanup continues if the caller times out; subsequent calls wait for the same cleanup operation.
func (*App) Signal ¶
Signal durably appends a JSON payload to one instance's named inbox and wakes it. Signals are accepted from instance creation until closure, including while an execution holds the lease. Channel names pass through unchanged (1–255 bytes).
type ApplicationError ¶
type ApplicationError struct {
Type string
Message string
NonRetryable bool
Details any
Cause error
}
ApplicationError carries a portable activity or workflow failure. Details must be JSON-serializable and is returned as JSON-decoded data.
func NewApplicationError ¶
func NewApplicationError(message, errType string, details any, cause error) *ApplicationError
NewApplicationError constructs a retryable application failure.
func NewNonRetryableApplicationError ¶
func NewNonRetryableApplicationError(message, errType string, details any, cause error) *ApplicationError
NewNonRetryableApplicationError constructs a failure that must not be retried.
func (*ApplicationError) Error ¶
func (e *ApplicationError) Error() string
Error returns the message, followed by the cause when one is set.
func (*ApplicationError) Unwrap ¶
func (e *ApplicationError) Unwrap() error
Unwrap returns the cause, so errors.Is and errors.As see through the error.
type Child ¶
type Child[O any] struct { // contains filtered or unexported fields }
Child is a child workflow outcome belonging to its issuing execution. Use its methods serially from the workflow coroutine.
func StartChild ¶
func StartChild[O any](ctx *WorkflowContext, workflow string, input any, opts ...ChildOption) (*Child[O], error)
StartChild starts a workflow by name. Registration is required only on a worker that executes the child. The parent command and child creation commit together. A locally cancelled scope records cancellation without creating an instance.
func (*Child[O]) Cancel ¶
func (c *Child[O]) Cancel(ctx *WorkflowContext) error
Cancel records one child_cancel command per handle, resolves an unfinished handle immediately, and requests cancellation of its running child.
func (*Child[O]) Get ¶
func (c *Child[O]) Get(ctx *WorkflowContext) (O, error)
Get returns the stored outcome and consumes no command position or delivery batch. Cancellation follows the issuing scope, regardless of ctx's scope. A stored outcome wins over cancellation.
type ChildOption ¶
type ChildOption func(*childOptions)
ChildOption configures a child workflow start.
func WithChildID ¶
func WithChildID(id string) ChildOption
WithChildID supplies a stable instance ID instead of parentID/historyKey.
type ExecuteOption ¶
type ExecuteOption func(*executeOptions)
ExecuteOption configures activity execution.
func WithActivityID ¶
func WithActivityID(id string) ExecuteOption
WithActivityID specifies a manual activity ID. Commands must be issued serially from workflow code. IDs starting with $ or # are reserved. Execute rejects these IDs with ErrHistoryKeyConflict before running the activity.
func WithActivityOptions ¶
func WithActivityOptions(options ActivityOptions) ExecuteOption
WithActivityOptions overrides non-zero fields for this activity reservation.
type FindInstancesOptions ¶
type FindInstancesOptions = storage.ListInstancesOptions
FindInstancesOptions filters and pages instances for App.FindInstancesWithOptions.
type Future ¶
type Future[O any] struct { // contains filtered or unexported fields }
Future is an activity outcome tied to the workflow execution that issued it. Use it serially from that workflow's coroutine. Get may be called repeatedly.
func (*Future[O]) Get ¶
func (f *Future[O]) Get(ctx *WorkflowContext) (O, error)
Get returns the stored outcome, or yields until it is available. It consumes no command position. A stored outcome takes precedence over cancellation.
type InstanceClosedError ¶
type InstanceClosedError = storage.InstanceClosedError
InstanceClosedError reports the terminal status that rejected a signal.
type LockAcquisitionError ¶
LockAcquisitionError indicates failure to acquire a workflow lock.
func (*LockAcquisitionError) Error ¶
func (e *LockAcquisitionError) Error() string
type NondeterminismError ¶
type NondeterminismError = replay.NondeterminismError
NondeterminismError reports a replay command divergence.
type Option ¶
type Option func(*appConfig)
Option configures an App.
func WithActivityPollInterval ¶
WithActivityPollInterval sets the base activity poll interval before adaptive backoff.
func WithActivityQueues ¶
WithActivityQueues restricts workers and inline attempts to these queues. Without this option an App serves every queue; an empty list serves none.
func WithAutoMigrate ¶
WithAutoMigrate enables or disables automatic database migrations on startup.
When enabled (default), selvedge will automatically apply pending dbmate-compatible migrations from the embedded internal/migrations/sql/ directory during App.Start().
This is compatible with the dbmate CLI tool and uses the same schema_migrations table for tracking applied migrations.
Set to false if you prefer to manage migrations manually using dbmate CLI:
dbmate -d internal/migrations/sql/sqlite up
func WithClock ¶
WithClock sets the wall clock used by Now and timer expiry. A nil clock uses time.Now. It does not change lease clocks or the timer poller.
func WithDatabase ¶
WithDatabase sets the database connection URL. Supported formats:
- SQLite: "file:path/to/db.db" or "sqlite://path/to/db.db"
- PostgreSQL: "postgres://user:pass@host:port/dbname"
func WithHooks ¶
func WithHooks(h hooks.WorkflowHooks) Option
WithHooks sets the workflow lifecycle hooks. A nil value installs NoOpHooks, replacing any hooks configured by earlier options.
func WithLeaderHeartbeatInterval ¶
WithLeaderHeartbeatInterval sets the interval for leader election heartbeat. The leader will renew its lease at this interval. Default: 15 seconds.
func WithLeaderLeaseDuration ¶
WithLeaderLeaseDuration sets the duration for which a leader holds its lease. Should be at least 3x the heartbeat interval to allow for missed heartbeats. Default: 45 seconds.
func WithListenNotify ¶
WithListenNotify configures PostgreSQL LISTEN/NOTIFY usage. - nil (default): auto-detect based on database URL (enabled for PostgreSQL) - true: force enable (fails if not PostgreSQL) - false: force disable (use polling only)
func WithMaxConcurrentActivities ¶
WithMaxConcurrentActivities bounds the number of background activity attempts.
func WithMaxConcurrentResumptions ¶
WithMaxConcurrentResumptions sets the maximum number of concurrent workflow resumptions. This limits goroutine spawning in background tasks to prevent resource exhaustion. Default: 10.
func WithMaxConcurrentTimers ¶
WithMaxConcurrentTimers sets the maximum number of concurrent timer handlers. Default: 10.
func WithMaxPollInterval ¶
WithMaxPollInterval caps workflow and activity polling delays, including jitter. The default is one minute. Nonpositive values leave the configured cap unchanged.
func WithMaxTimersPerBatch ¶
WithMaxTimersPerBatch sets the maximum number of timers to process per polling cycle. Default: 100.
func WithMaxWorkflowsPerBatch ¶
WithMaxWorkflowsPerBatch sets the maximum number of workflows to process per polling cycle. Default: 100.
func WithMigrationsFS ¶
WithMigrationsFS sets a custom filesystem for database migrations.
By default, selvedge uses embedded migrations from internal/migrations/sql/. Use this option to provide custom migrations from a different source.
The filesystem should contain subdirectories for each database type:
- sqlite/
- postgresql/
Each subdirectory should contain .sql files in dbmate format with -- migrate:up and -- migrate:down sections.
func WithNotifyReconnectDelay ¶
WithNotifyReconnectDelay sets the delay before reconnecting after a LISTEN/NOTIFY connection failure. Default: 60 seconds.
func WithServiceName ¶
WithServiceName sets the service name for identification.
func WithShutdownTimeout ¶
WithShutdownTimeout sets the timeout for graceful shutdown.
func WithTimerCheckInterval ¶
WithTimerCheckInterval sets the interval for checking expired timers.
func WithWaitPollInterval ¶
WithWaitPollInterval sets the completion wait fallback interval (default one second). Nonpositive values leave the configured interval unchanged.
func WithWorkerID ¶
WithWorkerID sets a custom worker ID. If not set, a UUID will be generated.
func WithWorkflowResumptionInterval ¶
WithWorkflowResumptionInterval sets the interval for the workflow resumption task. This task finds runnable workflows and expired leases, then dispatches them. This is essential for load balancing in multi-worker environments. Default: 1 second.
type QueryFailedError ¶
QueryFailedError wraps a handler error, panic, or JSON conversion failure.
func (*QueryFailedError) Error ¶
func (e *QueryFailedError) Error() string
func (*QueryFailedError) Unwrap ¶
func (e *QueryFailedError) Unwrap() error
type QueryNotFoundError ¶
type QueryNotFoundError struct{ Name string }
QueryNotFoundError means no handler of this name exists at the replay stop.
func (*QueryNotFoundError) Error ¶
func (e *QueryNotFoundError) Error() string
type RecordedEvent ¶
type RecordedEvent struct {
Seq int64 `json:"seq"`
Key string `json:"key"`
Command string `json:"command"`
EventType string `json:"event_type"`
Data json.RawMessage `json:"data,omitempty"`
DataType string `json:"data_type"`
Binary []byte `json:"binary"`
Reservation json.RawMessage `json:"reservation,omitempty"`
DeliveredSeq int64 `json:"delivered_seq"`
}
RecordedEvent contains one command or workflow-close history row.
type RecordedHistory ¶
type RecordedHistory struct {
Format string `json:"format"`
Instance RecordedInstance `json:"instance"`
Events []RecordedEvent `json:"events"`
Signals []RecordedSignal `json:"signals"`
}
RecordedHistory is a portable snapshot of the inputs needed to replay one instance.
func ExportHistory ¶
ExportHistory reads one consistent snapshot from an initialized App.
func ParseHistory ¶
func ParseHistory(data []byte) (*RecordedHistory, error)
ParseHistory strictly decodes the portable Selvedge history format.
type RecordedInstance ¶
type RecordedInstance struct {
InstanceID string `json:"instance_id"`
WorkflowName string `json:"workflow_name"`
Status WorkflowStatus `json:"status"`
Input json.RawMessage `json:"input,omitempty"`
Output json.RawMessage `json:"output,omitempty"`
CancelRequested bool `json:"cancel_requested"`
CancelReason string `json:"cancel_reason"`
ParentInstanceID string `json:"parent_instance_id"`
ParentKey string `json:"parent_key"`
}
RecordedInstance contains durable workflow metadata without execution ownership.
type RecordedSignal ¶
type RecordedSignal struct {
ID int64 `json:"id"`
Channel string `json:"channel"`
DeliveredSeq int64 `json:"delivered_seq"`
ConsumedSeq int64 `json:"consumed_seq"`
}
RecordedSignal preserves delivery and consumption positions without its payload.
type ReplayDecodeError ¶
ReplayDecodeError reports a recorded value that cannot be decoded into the Go output type. It is a workflow failure, not a retryable activity failure.
func (*ReplayDecodeError) Error ¶
func (e *ReplayDecodeError) Error() string
func (*ReplayDecodeError) Unwrap ¶
func (e *ReplayDecodeError) Unwrap() error
type ReplayError ¶
ReplayError preserves both the replay divergence and the live workflow error.
func (*ReplayError) Error ¶
func (e *ReplayError) Error() string
func (*ReplayError) Unwrap ¶
func (e *ReplayError) Unwrap() []error
Unwrap exposes the divergence and original Go error for errors.Is and errors.As.
type ReplayOutcomeError ¶
type ReplayOutcomeError struct {
InstanceID string
RecordedStatus, ReplayedStatus WorkflowStatus
Recorded, Replayed json.RawMessage
}
ReplayOutcomeError reports a workflow close that differs from its recorded outcome.
func (*ReplayOutcomeError) Error ¶
func (e *ReplayOutcomeError) Error() string
type RetryExhaustedError ¶
RetryExhaustedError indicates that all retry attempts have been exhausted.
func (*RetryExhaustedError) Error ¶
func (e *RetryExhaustedError) Error() string
func (*RetryExhaustedError) Unwrap ¶
func (e *RetryExhaustedError) Unwrap() error
type SleepOption ¶
type SleepOption func(*sleepOptions)
SleepOption configures sleep behavior.
func WithSleepID ¶
func WithSleepID(id string) SleepOption
WithSleepID sets a custom timer ID for the sleep. This is useful for identifying timers in logs and for deterministic replay. The same prefixes as WithActivityID are reserved; Sleep rejects them with ErrHistoryKeyConflict.
type StartOption ¶
type StartOption func(*startOptions)
StartOption configures workflow start options.
func WithInstanceID ¶
func WithInstanceID(id string) StartOption
WithInstanceID specifies a custom instance ID. If not provided, a UUID will be generated.
type TerminalError ¶
type TerminalError struct {
Err error
}
TerminalError wraps an error to indicate it should not be retried. When an activity returns a TerminalError, the workflow will fail without attempting any retries.
func NewTerminalError ¶
func NewTerminalError(err error) *TerminalError
NewTerminalError creates a new TerminalError wrapping the given error.
func NewTerminalErrorf ¶
func NewTerminalErrorf(format string, args ...any) *TerminalError
NewTerminalErrorf creates a new TerminalError with a formatted message.
func (*TerminalError) Error ¶
func (e *TerminalError) Error() string
func (*TerminalError) Unwrap ¶
func (e *TerminalError) Unwrap() error
type TimeoutKind ¶
type TimeoutKind string
TimeoutKind identifies which activity deadline elapsed.
const ( // TimeoutScheduleToStart means the scheduled task was not claimed in time. TimeoutScheduleToStart TimeoutKind = "schedule_to_start" // TimeoutStartToClose means the attempt exceeded its execution deadline. TimeoutStartToClose TimeoutKind = "start_to_close" // TimeoutHeartbeat means the activity claim expired without renewed progress. TimeoutHeartbeat TimeoutKind = "heartbeat" )
type Timer ¶
type Timer struct {
// contains filtered or unexported fields
}
Timer is a durable timer tied to its issuing workflow execution. Use it serially on the workflow coroutine. Get may be called repeatedly.
func NewTimer ¶
func NewTimer(ctx *WorkflowContext, d time.Duration, opts ...SleepOption) (*Timer, error)
NewTimer issues a sleep command without yielding. Nonpositive durations expire immediately; positive durations keep their first recorded deadline on replay.
func (*Timer) Get ¶
func (t *Timer) Get(ctx *WorkflowContext) (time.Time, error)
Get returns the recorded expiry or cancellation without consuming a command position or delivering a batch. A stored fire takes precedence over cancellation.
type Workflow ¶
type Workflow[I, O any] interface { // Name returns the unique name of the workflow. Name() string // Execute runs the workflow logic. Execute(ctx *WorkflowContext, input I) (O, error) }
Workflow defines the interface for a durable workflow. I is the input type, O is the output type.
func GetWorkflow ¶
GetWorkflow retrieves a registered workflow by name. Returns nil if not found.
type WorkflowCancelledError ¶
WorkflowCancelledError indicates that a workflow has been cancelled.
func (*WorkflowCancelledError) Error ¶
func (e *WorkflowCancelledError) Error() string
func (*WorkflowCancelledError) Unwrap ¶
func (e *WorkflowCancelledError) Unwrap() error
Unwrap makes workflow cancellation match context.Canceled.
type WorkflowContext ¶
type WorkflowContext struct {
// contains filtered or unexported fields
}
WorkflowContext provides context for workflow execution. It provides command positions, replay state, and recorded history.
func Detached ¶
func Detached(ctx *WorkflowContext) *WorkflowContext
Detached creates a parentless scope that ignores durable workflow cancellation. It retains the execution's lease and shutdown context. Use it for cleanup.
func NewWorkflowContext ¶
func NewWorkflowContext( ctx context.Context, instanceID, workerID, workflowName string, ) *WorkflowContext
NewWorkflowContext creates a standalone context without recorded history. Commands require an engine-managed workflow coroutine.
func NewWorkflowContextFromExecution ¶
func NewWorkflowContextFromExecution(execCtx *replay.ExecutionContext) *WorkflowContext
NewWorkflowContextFromExecution creates a WorkflowContext from a replay ExecutionContext.
func WithCancel ¶
func WithCancel(ctx *WorkflowContext) (scoped *WorkflowContext, cancel func())
WithCancel creates a child cancellation scope without issuing a command. Its cancel function must run on the workflow coroutine, outside predicates. The first call records one cancel_scope command, even for an empty scope.
func (*WorkflowContext) App ¶
func (c *WorkflowContext) App() *App
App returns the App reference for this workflow context. Returns nil if not set.
func (*WorkflowContext) Context ¶
func (c *WorkflowContext) Context() context.Context
Context returns the underlying context.Context.
func (*WorkflowContext) Done ¶
func (c *WorkflowContext) Done() <-chan struct{}
Done signals lease or shutdown cancellation, never scope cancellation.
func (*WorkflowContext) Err ¶
func (c *WorkflowContext) Err() error
Err reports lease or shutdown cancellation, never scope cancellation.
func (*WorkflowContext) Hooks ¶
func (c *WorkflowContext) Hooks() hooks.WorkflowHooks
Hooks returns the workflow hooks for observability. Returns nil if hooks are not available (e.g., not using replay engine).
func (*WorkflowContext) InstanceID ¶
func (c *WorkflowContext) InstanceID() string
InstanceID returns the workflow instance ID.
func (*WorkflowContext) IsReplaying ¶
func (c *WorkflowContext) IsReplaying() bool
IsReplaying returns true if the workflow is being replayed.
func (*WorkflowContext) SetApp ¶
func (c *WorkflowContext) SetApp(app *App)
SetApp sets the App reference for this workflow context. This is typically called internally when creating the context.
func (*WorkflowContext) Storage ¶
func (c *WorkflowContext) Storage() WorkflowStorage
Storage returns read-only history access, or nil outside the replay engine.
func (*WorkflowContext) WithContext ¶
func (c *WorkflowContext) WithContext(ctx context.Context) *WorkflowContext
WithContext returns a copy of the WorkflowContext with a new underlying context.
func (*WorkflowContext) WorkerID ¶
func (c *WorkflowContext) WorkerID() string
WorkerID returns the worker ID that is executing this workflow.
func (*WorkflowContext) WorkflowName ¶
func (c *WorkflowContext) WorkflowName() string
WorkflowName returns the workflow name.
type WorkflowDescription ¶
type WorkflowDescription struct {
InstanceID, WorkflowName string
Status WorkflowStatus
CancelRequested bool
CancelReason, ParentInstanceID, ParentKey string
StartedAt, UpdatedAt time.Time
}
WorkflowDescription exposes durable status and parent metadata.
type WorkflowFunc ¶
type WorkflowFunc[I, O any] struct { // contains filtered or unexported fields }
WorkflowFunc is a convenience type for workflows defined as functions.
func DefineWorkflow ¶
func DefineWorkflow[I, O any](name string, fn func(ctx *WorkflowContext, input I) (O, error)) *WorkflowFunc[I, O]
DefineWorkflow creates a new workflow from a function.
func (*WorkflowFunc[I, O]) Execute ¶
func (w *WorkflowFunc[I, O]) Execute(ctx *WorkflowContext, input I) (O, error)
Execute runs the workflow function.
func (*WorkflowFunc[I, O]) Name ¶
func (w *WorkflowFunc[I, O]) Name() string
Name returns the workflow name.
type WorkflowNotFoundError ¶
type WorkflowNotFoundError struct {
InstanceID string
}
WorkflowNotFoundError indicates that a workflow instance was not found.
func (*WorkflowNotFoundError) Error ¶
func (e *WorkflowNotFoundError) Error() string
func (*WorkflowNotFoundError) Unwrap ¶
func (e *WorkflowNotFoundError) Unwrap() error
Unwrap makes a missing workflow match ErrInstanceNotFound.
type WorkflowNotRegisteredError ¶
type WorkflowNotRegisteredError struct{ WorkflowName string }
WorkflowNotRegisteredError means the querying App lacks the workflow runner.
func (*WorkflowNotRegisteredError) Error ¶
func (e *WorkflowNotRegisteredError) Error() string
type WorkflowPanicError ¶
type WorkflowPanicError = replay.WorkflowPanicError
WorkflowPanicError preserves a recovered workflow panic and its stack.
type WorkflowResult ¶
WorkflowResult represents the result of a workflow execution.
func GetWorkflowResult ¶
func GetWorkflowResult[O any](ctx context.Context, app *App, instanceID string) (*WorkflowResult[O], error)
GetWorkflowResult retrieves the result of a workflow instance.
func WaitWorkflow ¶
func WaitWorkflow[O any](ctx context.Context, app *App, instanceID string) (*WorkflowResult[O], error)
WaitWorkflow waits for a terminal result. Workflow failures are in Result.Error. Cancellation of ctx stops only the wait. GetWorkflowResult is the nonblocking form.
type WorkflowStatus ¶
type WorkflowStatus = storage.WorkflowStatus
WorkflowStatus is the durable state of a workflow instance.
type WorkflowStorage ¶
type WorkflowStorage interface {
GetHistoryEvent(context.Context, string, string) (*storage.HistoryEvent, error)
}
WorkflowStorage exposes recorded history for diagnostics. Workflow commands must use the workflow APIs rather than direct database writes.
Source Files
¶
- activity.go
- activity_options.go
- activity_tasks.go
- activity_timeouts.go
- activity_worker.go
- app.go
- await.go
- child.go
- client.go
- context.go
- decode.go
- describe.go
- error_envelope.go
- errors.go
- events.go
- future.go
- heartbeat.go
- migrations_embed.go
- now.go
- options.go
- query.go
- receive_async.go
- receive_timeout.go
- replay_harness.go
- scope.go
- signals.go
- timer.go
- workflow.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package hooks provides lifecycle hooks for workflow observability.
|
Package hooks provides lifecycle hooks for workflow observability. |
|
otel
Package otel exports Selvedge lifecycle callbacks as completed OpenTelemetry spans.
|
Package otel exports Selvedge lifecycle callbacks as completed OpenTelemetry spans. |
|
internal
|
|
|
migrations
Package migrations provides automatic dbmate-compatible migration support.
|
Package migrations provides automatic dbmate-compatible migration support. |
|
notify
Package notify provides PostgreSQL LISTEN/NOTIFY functionality for real-time notifications.
|
Package notify provides PostgreSQL LISTEN/NOTIFY functionality for real-time notifications. |
|
storage
Package storage provides the storage layer for Selvedge.
|
Package storage provides the storage layer for Selvedge. |
|
storage/faultstore
Package faultstore provides storage fault injection for engine tests.
|
Package faultstore provides storage fault injection for engine tests. |
|
testkit
Package testkit provides deterministic execution clocks for tests.
|
Package testkit provides deterministic execution clocks for tests. |
|
Package retry provides retry policies for activities.
|
Package retry provides retry policies for activities. |