Documentation
¶
Overview ¶
Package dispatch provides a composable, extensible durable execution engine for Go. It offers library-first background jobs, workflow orchestration, lifecycle hooks, and distributed workers.
Dispatch is designed as a library, not a service. Import it, configure a store, and register jobs or workflows as ordinary Go functions.
Quick Start ¶
d, err := dispatch.New(
dispatch.WithStore(pgStore),
dispatch.WithConcurrency(20),
)
Architecture ¶
Dispatch follows a composable store pattern where each subsystem (job, workflow, cron, dlq, event, cluster, artifact) defines its own store interface. A single backend implements all of them.
Artifacts ¶
Jobs that process large files use the artifact plane rather than the payload column: an artifact is a tracked reference to an object in external storage, declared as a job input and staged to a content-addressed local cache before the handler runs. See the artifact package. It is opt-in — with no backend configured, Dispatch behaves as it did before artifacts existed.
All entity IDs use TypeID — type-prefixed, K-sortable, UUIDv7-based, compile-time safe identifiers.
Index ¶
- Variables
- type Config
- type Context
- type Dispatcher
- func (d *Dispatcher) BeforeStoreClose(stop func(context.Context) error)
- func (d *Dispatcher) Config() Config
- func (d *Dispatcher) Logger() log.Logger
- func (d *Dispatcher) SetExtensions(e extensionEmitter)
- func (d *Dispatcher) SetPool(p poolRunner)
- func (d *Dispatcher) Start(ctx context.Context) error
- func (d *Dispatcher) Stop(ctx context.Context) error
- func (d *Dispatcher) Store() Storer
- type Entity
- type ID
- type Option
- func WithConcurrency(n int) Option
- func WithCronLeaderTTL(d time.Duration) Option
- func WithCronLockTTL(d time.Duration) Option
- func WithCronRefreshInterval(d time.Duration) Option
- func WithCronStoreCallTimeout(d time.Duration) Option
- func WithCronTickInterval(d time.Duration) Option
- func WithHeartbeatInterval(d time.Duration) Option
- func WithLogger(l log.Logger) Option
- func WithMaxPollInterval(d time.Duration) Option
- func WithPollInterval(d time.Duration) Option
- func WithQueues(queues []string) Option
- func WithStaleJobThreshold(d time.Duration) Option
- func WithStore(s Storer) Option
- func WithWorkerStoreCallTimeout(d time.Duration) Option
- type Prefix
- type Storer
Constants ¶
This section is empty.
Variables ¶
var ( // Store errors. ErrNoStore = errors.New("dispatch: no store configured") ErrStoreClosed = errors.New("dispatch: store closed") ErrMigrationFailed = errors.New("dispatch: migration failed") // Not found errors. ErrJobNotFound = errors.New("dispatch: job not found") ErrWorkflowNotFound = errors.New("dispatch: workflow not found") ErrRunNotFound = errors.New("dispatch: run not found") ErrCronNotFound = errors.New("dispatch: cron entry not found") ErrDLQNotFound = errors.New("dispatch: dlq entry not found") ErrEventNotFound = errors.New("dispatch: event not found") ErrWorkerNotFound = errors.New("dispatch: worker not found") // Conflict errors. ErrJobAlreadyExists = errors.New("dispatch: job already exists") ErrDuplicateCron = errors.New("dispatch: duplicate cron entry") // ErrDLQAlreadyExists is what PushDLQ returns for an entry ID that is // already in the dead letter queue. A push never overwrites an entry. ErrDLQAlreadyExists = errors.New("dispatch: dlq entry already exists") // ErrDLQAlreadyReplayed is what dlq.ReplayClaimer.ClaimReplay returns // when the entry was claimed by an earlier replay or retry, so the // same failure is never turned back into work twice. ErrDLQAlreadyReplayed = errors.New("dispatch: dlq entry already replayed") // State errors. ErrInvalidState = errors.New("dispatch: invalid state transition") ErrMaxRetriesExceeded = errors.New("dispatch: max retries exceeded") // ErrPermanent marks a failure that retrying cannot resolve. A job // whose handler returns an error wrapping it skips its remaining // attempts and goes straight to the dead letter queue. // // Handlers wrap it to decline a retry they know is pointless: // // if !validPayload(p) { // return fmt.Errorf("malformed payload: %w", dispatch.ErrPermanent) // } // // The artifact plane wraps it for a missing or forbidden input, which // is where it earns its keep: a job whose input was deleted would // otherwise spend every attempt, and the whole backoff schedule // between them, rediscovering that the object is still gone. // // Only mark a failure permanent when retrying is certain to fail the // same way. Anything unrecognized stays retryable on purpose: a job // retried needlessly costs some compute, while one dead-lettered by // mistake loses work that would have succeeded. ErrPermanent = errors.New("dispatch: permanent failure") // Cluster errors. ErrLeadershipLost = errors.New("dispatch: leadership lost") ErrNotLeader = errors.New("dispatch: not the leader") )
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// Concurrency is the maximum number of jobs processed concurrently.
Concurrency int
// Queues is the list of queues this dispatcher will poll.
Queues []string
// PollInterval is how often to poll for new jobs.
PollInterval time.Duration
// MaxPollInterval caps the fetcher's idle backoff. When polls come
// back empty the interval doubles from PollInterval up to this value,
// so an idle dispatcher does not hit the store every PollInterval.
// In-process enqueues wake the fetcher immediately. Default 30s.
MaxPollInterval time.Duration
// ShutdownTimeout is the maximum time to wait for graceful shutdown.
ShutdownTimeout time.Duration
// HeartbeatInterval is how often running jobs send heartbeats.
HeartbeatInterval time.Duration
// StaleJobThreshold is how long before a job without heartbeat is
// considered stale.
StaleJobThreshold time.Duration
// ReapInterval is how often the pool scans for expired leases. Zero
// uses worker.DefaultReapInterval. This is the scan cadence, not the
// lease duration.
ReapInterval time.Duration
// DefaultLeaseTTL is how far each renewal pushes a job's lease expiry
// when the job declares none of its own. Zero falls back to
// StaleJobThreshold, preserving existing reclamation timing.
DefaultLeaseTTL time.Duration
// WorkerStoreCallTimeout caps a single worker store roundtrip
// (DequeueJobs, HeartbeatJob, ReapStaleJobs, UpdateJob). Bounds
// how long a stalled driver session can hold a pool connection
// before the worker abandons it. Zero leaves the worker default
// (5s); negative disables (test-only).
WorkerStoreCallTimeout time.Duration
// CronTickInterval controls how often the cron scheduler checks
// for due entries. Default 1s. Ticks run against in-memory
// leadership and cron-list caches, so they cost no store traffic
// between refreshes.
CronTickInterval time.Duration
// CronLeaderTTL is the TTL for the cron leader election lock.
// Default 60s. The leader loop renews at half this interval;
// failover after a leader crash takes up to the TTL.
CronLeaderTTL time.Duration
// CronRefreshInterval is how often the cron leader re-lists entries
// from the store. Between refreshes ticks run against an in-memory
// cache; in-process registrations invalidate it immediately.
// Default 30s.
CronRefreshInterval time.Duration
// CronLockTTL is the per-entry distributed lock TTL fired around
// each cron entry execution. Default 30s.
CronLockTTL time.Duration
// CronStoreCallTimeout mirrors WorkerStoreCallTimeout for the
// cron scheduler's GetLeader / ListCrons / Acquire/Release lock
// paths. Default 5s; negative disables (test-only).
CronStoreCallTimeout time.Duration
}
Config holds configuration for the Dispatcher.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns a Config with sensible defaults.
type Context ¶
Context is the execution context for Dispatch handlers. For Phase 2 it is a simple alias for context.Context. Scope is injected via forge.WithScope on the stdlib context. A richer dispatch-specific context (with step methods for workflows) is planned for a later phase.
type Dispatcher ¶
type Dispatcher struct {
// contains filtered or unexported fields
}
Dispatcher is the central coordinator for job processing, workflows, cron scheduling, and distributed coordination.
Create one with New() and functional options. The Dispatcher holds references to subsystem components via internal interfaces to avoid import cycles. Use the Setup() function from the dispatch/setup package to wire everything together.
func New ¶
func New(opts ...Option) (*Dispatcher, error)
New creates a new Dispatcher with the given options.
func (*Dispatcher) BeforeStoreClose ¶
func (d *Dispatcher) BeforeStoreClose(stop func(context.Context) error)
BeforeStoreClose installs a required drain step after workers and shutdown hooks finish. Configure it before Start. Failure propagates through Stop.
func (*Dispatcher) Config ¶
func (d *Dispatcher) Config() Config
Config returns a copy of the dispatcher's configuration.
func (*Dispatcher) Logger ¶
func (d *Dispatcher) Logger() log.Logger
Logger returns the dispatcher's logger.
func (*Dispatcher) SetExtensions ¶
func (d *Dispatcher) SetExtensions(e extensionEmitter)
SetExtensions sets the extension emitter (called by setup package).
func (*Dispatcher) SetPool ¶
func (d *Dispatcher) SetPool(p poolRunner)
SetPool sets the worker pool (called by setup package).
func (*Dispatcher) Start ¶
func (d *Dispatcher) Start(ctx context.Context) error
Start begins job processing.
func (*Dispatcher) Stop ¶
func (d *Dispatcher) Stop(ctx context.Context) error
Stop gracefully shuts down the dispatcher.
func (*Dispatcher) Store ¶
func (d *Dispatcher) Store() Storer
Store returns the dispatcher's store.
type Entity ¶
type Entity struct {
CreatedAt time.Time `json:"created_at" bun:"created_at,notnull,default:current_timestamp"`
UpdatedAt time.Time `json:"updated_at" bun:"updated_at,notnull,default:current_timestamp"`
}
Entity is the base type embedded by all dispatch domain objects.
type Option ¶
type Option func(*Dispatcher) error
Option configures a Dispatcher.
func WithConcurrency ¶
WithConcurrency sets the maximum number of concurrent job processors.
func WithCronLeaderTTL ¶
WithCronLeaderTTL sets the leader-election lock TTL. The leader loop renews at half this interval, so 60s = renew every 30s. Default 60s; failover after a leader crash takes up to the TTL.
func WithCronLockTTL ¶
WithCronLockTTL sets the per-entry distributed lock TTL fired around each cron entry execution. Default 30s.
func WithCronRefreshInterval ¶
WithCronRefreshInterval sets how often the cron leader re-lists entries from the store. Between refreshes ticks run against an in-memory cache; in-process registrations invalidate it immediately. Default 30s.
func WithCronStoreCallTimeout ¶
WithCronStoreCallTimeout caps a single cron-scheduler store roundtrip (GetLeader, RenewLeadership, ListCrons, Acquire/Release lock, Update entry). Default 5s; negative disables (test-only).
func WithCronTickInterval ¶
WithCronTickInterval sets how often the cron scheduler checks for due entries. Default 1s. Ticks run against in-memory leadership and cron-list caches, so they cost no store traffic between refreshes.
func WithHeartbeatInterval ¶
WithHeartbeatInterval sets how often running jobs send heartbeats. Default 10s. Set to 0 to disable heartbeats entirely (workers won't extend lease on long-running jobs).
func WithLogger ¶
WithLogger sets the structured logger for the dispatcher.
func WithMaxPollInterval ¶
WithMaxPollInterval caps the worker fetcher's idle backoff. Empty polls double the poll interval from PollInterval up to this value; new work or an in-process enqueue resets it. Default 30s.
func WithPollInterval ¶
WithPollInterval sets how often each worker polls for new jobs. Lower values reduce job pickup latency at the cost of constant driver-pool pressure. Default 1s.
func WithQueues ¶
WithQueues sets the queues the dispatcher will poll.
func WithStaleJobThreshold ¶
WithStaleJobThreshold sets how often the reaper runs, and how long without a heartbeat a job may go before it is reaped. Default 30s. Set to 0 to disable reaping entirely.
The second half of that no longer applies to a store implementing job.LeaseStore, which is all five built-in backends. There the window before a job is taken back is the lease TTL, per job, from WithLeaseTTL or the pool default, and this value only decides how frequently the reaper looks and whether it runs at all. It still governs the reap window outright for any other store.
func WithStore ¶
WithStore sets the persistence backend for the dispatcher. The store must implement Storer at minimum; typically it will be a store.Store which embeds all subsystem store interfaces.
func WithWorkerStoreCallTimeout ¶
WithWorkerStoreCallTimeout caps a single worker store roundtrip. Bounds how long a stalled driver session can hold a pool connection before the worker abandons it. Default 5s; negative disables (test-only).
type Storer ¶
type Storer interface {
Migrate(ctx context.Context) error
Ping(ctx context.Context) error
Close() error
}
Storer is the minimal store interface held by the Dispatcher. It covers lifecycle operations only. The full composite interface (store.Store) is used in subsystem layers that don't create import cycles. Implementations satisfy store.Store which embeds all subsystem stores.
Directories
¶
| Path | Synopsis |
|---|---|
|
components
templ: version: v0.3.1001
|
templ: version: v0.3.1001 |
|
pages
templ: version: v0.3.1001
|
templ: version: v0.3.1001 |
|
settings
templ: version: v0.3.1001
|
templ: version: v0.3.1001 |
|
shared
Package shared provides types used across dashboard sub-packages.
|
Package shared provides types used across dashboard sub-packages. |
|
widgets
templ: version: v0.3.1001
|
templ: version: v0.3.1001 |
|
_examples
|
|
|
kitchen-sink
command
Package main demonstrates a comprehensive Dispatch setup with jobs, workflows, cron scheduling, saga compensations, child workflows, and a DWP WebSocket server.
|
Package main demonstrates a comprehensive Dispatch setup with jobs, workflows, cron scheduling, saga compensations, child workflows, and a DWP WebSocket server. |
|
resources
command
Package main demonstrates resource-aware admission: one job definition whose memory requirement scales with its input, enqueued twice against a worker that cannot hold both at once.
|
Package main demonstrates resource-aware admission: one job definition whose memory requirement scales with its input, enqueued twice against a worker that cannot hold both at once. |
|
Package api provides HTTP handlers for the Dispatch API.
|
Package api provides HTTP handlers for the Dispatch API. |
|
Package artifact defines Dispatch's data plane: tracked references to objects in external storage, the pluggable Backend interface those objects live behind, and the Store contract that persists their metadata and ownership links.
|
Package artifact defines Dispatch's data plane: tracked references to objects in external storage, the pluggable Backend interface those objects live behind, and the Store contract that persists their metadata and ownership links. |
|
artifacttest
Package artifacttest provides a shared conformance suite and test doubles for artifact storage.
|
Package artifacttest provides a shared conformance suite and test doubles for artifact storage. |
|
cache
Package cache is the worker-local staging cache for artifact inputs.
|
Package cache is the worker-local staging cache for artifact inputs. |
|
staging
Package staging wires the artifact plane into job execution.
|
Package staging wires the artifact plane into job execution. |
|
sweeper
Package sweeper reclaims the storage Dispatch owns.
|
Package sweeper reclaims the storage Dispatch owns. |
|
trove
Package trove adapts a *trove.Trove instance as an artifact.Backend.
|
Package trove adapts a *trove.Trove instance as an artifact.Backend. |
|
Package audithook is a Dispatch extension that bridges lifecycle events to an immutable audit trail backend such as Chronicle.
|
Package audithook is a Dispatch extension that bridges lifecycle events to an immutable audit trail backend such as Chronicle. |
|
Package backoff provides pluggable retry delay strategies for job execution.
|
Package backoff provides pluggable retry delay strategies for job execution. |
|
Package client provides a Go client for connecting to a remote Dispatch instance via the Dispatch Wire Protocol (DWP) over WebSocket.
|
Package client provides a Go client for connecting to a remote Dispatch instance via the Dispatch Wire Protocol (DWP) over WebSocket. |
|
Package cluster provides distributed worker coordination, consensus-based leader election, worker registration, and work stealing / rebalancing.
|
Package cluster provides distributed worker coordination, consensus-based leader election, worker registration, and work stealing / rebalancing. |
|
k8s
Package k8s provides a Kubernetes-native cluster.Store implementation.
|
Package k8s provides a Kubernetes-native cluster.Store implementation. |
|
Package cron provides distributed cron scheduling with leader election and per-tenant support.
|
Package cron provides distributed cron scheduling with leader election and per-tenant support. |
|
Package dlq provides the dead letter queue for jobs that have exhausted their retry budget.
|
Package dlq provides the dead letter queue for jobs that have exhausted their retry budget. |
|
Package durable defines the transactional persistence contract for execution history and tasks.
|
Package durable defines the transactional persistence contract for execution history and tasks. |
|
delivery
Package delivery publishes accepted outbox intents independently of workflow workers.
|
Package delivery publishes accepted outbox intents independently of workflow workers. |
|
delivery/ecosystem
Package ecosystem maps Dispatch envelopes to reliable ecosystem acceptance APIs.
|
Package ecosystem maps Dispatch envelopes to reliable ecosystem acceptance APIs. |
|
durabletest
Package durabletest checks the execution store's atomicity and lease contract.
|
Package durabletest checks the execution store's atomicity and lease contract. |
|
runtime
Package runtime evaluates durable workflow decisions against recorded history.
|
Package runtime evaluates durable workflow decisions against recorded history. |
|
Package dwp implements the Dispatch Wire Protocol (DWP) — a message-based protocol for client↔server and server↔server communication.
|
Package dwp implements the Dispatch Wire Protocol (DWP) — a message-based protocol for client↔server and server↔server communication. |
|
Package engine wires all Dispatch subsystems together and provides the primary application-level API for registering and enqueuing work.
|
Package engine wires all Dispatch subsystems together and provides the primary application-level API for registering and enqueuing work. |
|
Package event provides the event bus for workflow coordination, supporting both in-memory and store-backed event publish/subscribe with WaitForEvent.
|
Package event provides the event bus for workflow coordination, supporting both in-memory and store-backed event publish/subscribe with WaitForEvent. |
|
examples
|
|
|
durable-async
command
This example uses memory for development.
|
This example uses memory for development. |
|
durable-cancellation
command
This example uses memory for development.
|
This example uses memory for development. |
|
durable-children
command
This example uses memory for development.
|
This example uses memory for development. |
|
durable-continuation
command
This example uses memory so you can run it without a database.
|
This example uses memory so you can run it without a database. |
|
durable-queries
command
This example uses memory for development.
|
This example uses memory for development. |
|
durable-selectors
command
This example uses memory for development.
|
This example uses memory for development. |
|
durable-signals
command
This example uses memory for development.
|
This example uses memory for development. |
|
durable-timeouts
command
This example uses memory for development.
|
This example uses memory for development. |
|
durable-workflow-cancellation
command
This example uses memory for development.
|
This example uses memory for development. |
|
durable-workflow-retry
command
Run this example with the memory store to inspect a failed run and its retry.
|
Run this example with the memory store to inspect a failed run and its retry. |
|
Package exec defines the execution boundary between the Dispatch worker and a job handler.
|
Package exec defines the execution boundary between the Dispatch worker and a job handler. |
|
exectest
Package exectest is the conformance suite every exec.Executor must pass.
|
Package exectest is the conformance suite every exec.Executor must pass. |
|
inproc
Package inproc runs job handlers in the worker process.
|
Package inproc runs job handlers in the worker process. |
|
shim
Package shim is the child side of out-of-process execution.
|
Package shim is the child side of out-of-process execution. |
|
subprocess
Package subprocess runs job handlers in a re-exec'd child process.
|
Package subprocess runs job handlers in a re-exec'd child process. |
|
wire
Package wire carries execution requests and results across a process boundary.
|
Package wire carries execution requests and results across a process boundary. |
|
Package ext defines the extension system for Dispatch.
|
Package ext defines the extension system for Dispatch. |
|
Package extension provides the Forge extension adapter for Dispatch.
|
Package extension provides the Forge extension adapter for Dispatch. |
|
contract
Package contract serves the operator-wide Dispatch dashboard.
|
Package contract serves the operator-wide Dispatch dashboard. |
|
Package id defines TypeID-based identity types for all Dispatch entities.
|
Package id defines TypeID-based identity types for all Dispatch entities. |
|
internal
|
|
|
audittest
Package audittest composes real memory audit acceptance for positive fixtures.
|
Package audittest composes real memory audit acceptance for positive fixtures. |
|
metricstest
Package metricstest is a go-utils metric factory for Dispatch's tests.
|
Package metricstest is a go-utils metric factory for Dispatch's tests. |
|
oteltest
Package oteltest installs a span recorder for Dispatch's tests and checks what the recorded spans may and may not carry.
|
Package oteltest installs a span recorder for Dispatch's tests and checks what the recorded spans may and may not carry. |
|
Package job defines the job entity, state machine, typed definitions, and store interface.
|
Package job defines the job entity, state machine, typed definitions, and store interface. |
|
jobtest
Package jobtest provides shared conformance suites for the optional job store capabilities.
|
Package jobtest provides shared conformance suites for the optional job store capabilities. |
|
Package middleware provides composable middleware for job execution.
|
Package middleware provides composable middleware for job execution. |
|
Package observability provides the legacy lifecycle counters for Dispatch.
|
Package observability provides the legacy lifecycle counters for Dispatch. |
|
Package operator exposes authorized, payload-safe durable execution reads.
|
Package operator exposes authorized, payload-safe durable execution reads. |
|
Package paging holds what every cursor-paged store list shares.
|
Package paging holds what every cursor-paged store list shares. |
|
qualification
module
|
|
|
Package queue defines the queue abstraction with priority ordering and per-queue / per-tenant rate limiting.
|
Package queue defines the queue abstraction with priority ordering and per-queue / per-tenant rate limiting. |
|
Package relayhook bridges Dispatch lifecycle events to Relay for webhook delivery.
|
Package relayhook bridges Dispatch lifecycle events to Relay for webhook delivery. |
|
Package resource defines Dispatch's resource model: what a job needs, what a worker has, and the admission control between them.
|
Package resource defines Dispatch's resource model: what a job needs, what a worker has, and the admission control between them. |
|
resourcetest
Package resourcetest provides fakes for testing resource-aware code: a deterministic clock, a lease-based reclaimer, and a scripted estimator.
|
Package resourcetest provides fakes for testing resource-aware code: a deterministic clock, a lease-based reclaimer, and a scripted estimator. |
|
Package scope provides helpers to capture and restore multi-tenant execution context (app and org identity) from/to context.Context.
|
Package scope provides helpers to capture and restore multi-tenant execution context (app and org identity) from/to context.Context. |
|
Package security defines the authorization boundary for remote Dispatch operators.
|
Package security defines the authorization boundary for remote Dispatch operators. |
|
Package store defines the aggregate persistence interface.
|
Package store defines the aggregate persistence interface. |
|
memory
Package memory implements an in-memory store for unit testing.
|
Package memory implements an in-memory store for unit testing. |
|
mongo
Package mongo implements store.Store using the grove ORM with MongoDB driver.
|
Package mongo implements store.Store using the grove ORM with MongoDB driver. |
|
postgres
Package postgres implements store.Store using the grove ORM with PostgreSQL dialect.
|
Package postgres implements store.Store using the grove ORM with PostgreSQL dialect. |
|
redis
Package redis implements store.Store using Grove KV backed by Redis.
|
Package redis implements store.Store using Grove KV backed by Redis. |
|
sqlite
Package sqlite implements store.Store using the Grove ORM with SQLite.
|
Package sqlite implements store.Store using the Grove ORM with SQLite. |
|
storetest
Package storetest provides conformance suites that every Dispatch store backend must pass.
|
Package storetest provides conformance suites that every Dispatch store backend must pass. |
|
Package stream provides a real-time event broker for Dispatch lifecycle events.
|
Package stream provides a real-time event broker for Dispatch lifecycle events. |
|
Package telemetry holds the OpenTelemetry spans and metric instruments Dispatch records for the work it does itself: the job pool and runner, cron, checkpoint workflows, the durable runtime, the outbox publisher and the artifact sweeper.
|
Package telemetry holds the OpenTelemetry spans and metric instruments Dispatch records for the work it does itself: the job pool and runner, cron, checkpoint workflows, the durable runtime, the outbox publisher and the artifact sweeper. |
|
Package worker provides the job execution engine — a Runner that orchestrates a single job attempt through middleware and an exec.Executor, and a Pool that manages concurrent worker goroutines polling for jobs.
|
Package worker provides the job execution engine — a Runner that orchestrates a single job attempt through middleware and an exec.Executor, and a Pool that manages concurrent worker goroutines polling for jobs. |
|
Package workflow defines typed workflow definitions, runs, step checkpointing, and the workflow store interface.
|
Package workflow defines typed workflow definitions, runs, step checkpointing, and the workflow store interface. |