Help us shape the future of Go! Take the 2026 Go Developer Survey

dispatch

package module
v1.9.3 Latest Latest
Warning

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

Go to latest
Published: Oct 26, 2026 License: MIT Imports: 6 Imported by: 0

README

Dispatch, a Go library for durable background jobs, workflows, cron scheduling and distributed workers

Dispatch: durable background jobs, workflows and cron for Go

Dispatch is an open-source durable execution library for Go. You import it, hand it a store, and register background jobs, multi-step workflows and cron schedules as plain Go functions. Retries with backoff, a dead letter queue, distributed workers with leader-elected cron, checkpointed workflows that resume after a crash, and a history-based durable workflow runtime with signals, queries and child workflows all run inside your own process, against PostgreSQL, SQLite, MongoDB, Redis or memory. There's no separate server to deploy. It runs standalone or as a Forge extension with a REST admin API.

CI Go Reference GitHub Stars

Documentation · Quick start · Features · Jobs · Workflows · Cron · Stores · Forge · Examples · FAQ

Quick start

You need Go 1.26 or later. This uses the in-memory store, so there's nothing else to install:

mkdir hello-dispatch
cd hello-dispatch
go mod init example.com/hello-dispatch
go get github.com/xraph/dispatch@latest

Save this as main.go:

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/xraph/dispatch"
	"github.com/xraph/dispatch/engine"
	"github.com/xraph/dispatch/job"
	"github.com/xraph/dispatch/store/memory"
	"github.com/xraph/dispatch/workflow"
)

type Signup struct {
	Email string `json:"email"`
}

var done = make(chan struct{})

var SendWelcome = job.NewDefinition("send_welcome",
	func(ctx context.Context, in Signup) error {
		fmt.Println("job: welcome email to", in.Email)
		return nil
	},
)

var Onboard = workflow.NewWorkflow("onboard",
	func(wf *workflow.Workflow, in Signup) error {
		if err := wf.Step("create-account", func(ctx context.Context) error {
			fmt.Println("workflow: account created for", in.Email)
			return nil
		}); err != nil {
			return err
		}

		return wf.Step("notify", func(ctx context.Context) error {
			fmt.Println("workflow: team notified")
			close(done)
			return nil
		})
	},
)

func main() {
	ctx := context.Background()

	d, err := dispatch.New(
		dispatch.WithStore(memory.New()),
		dispatch.WithConcurrency(4),
	)
	if err != nil {
		log.Fatal(err)
	}

	eng, err := engine.Build(d)
	if err != nil {
		log.Fatal(err)
	}
	engine.Register(eng, SendWelcome)
	engine.RegisterWorkflow(eng, Onboard)

	if err := d.Start(ctx); err != nil {
		log.Fatal(err)
	}
	defer d.Stop(ctx)

	in := Signup{Email: "ada@example.com"}
	if _, err := engine.Enqueue(ctx, eng, "send_welcome", in, job.WithMaxRetries(5)); err != nil {
		log.Fatal(err)
	}
	if _, err := engine.StartWorkflow(ctx, eng, "onboard", in); err != nil {
		log.Fatal(err)
	}

	select {
	case <-done:
		time.Sleep(200 * time.Millisecond) // give the job worker a moment
	case <-time.After(10 * time.Second):
		log.Fatal("timed out")
	}
}

Run it:

go run .
# workflow: account created for ada@example.com
# workflow: team notified
# job: welcome email to ada@example.com

The workflow prints first because StartWorkflow runs it in the calling goroutine, checkpointing each step as it goes, while the job waits in the queue for one of the four workers to poll it. Payloads are JSON, so whatever you pass to Enqueue has to marshal cleanly.

The memory store is for development and tests. Swap in postgres.New(groveDB) from store/postgres (see Stores) and the same jobs, workflow checkpoints and cron entries survive a restart and spread across every process that runs the engine.

Why Dispatch?

Most job queues give you jobs, and you build workflows, schedules and failure handling on top. Dispatch gives you all of it as a library inside your Go service, so a job handler is a typed function you can unit test and the queue is a table in the database you already run.

You need Dispatch gives you
Background jobs Typed job.Definition[T] handlers with queues, priority, delayed RunAt, timeouts and retries
Sane retries Constant, linear, exponential and jittered backoff (exponential with jitter, 1s to 1m, by default)
A place for jobs that keep failing A dead letter queue you can list, replay one by one or in bulk, and purge. Wrap an error in dispatch.ErrPermanent to skip the remaining retries
Multi-step processes Checkpointed workflows with Step, Parallel, Sleep, WaitForEvent and compensating steps for sagas, resumed from checkpoints after a restart
Long-running, signal-driven workflows A history-based durable runtime with activities, timers, signals, queries, child workflows, continue-as-new and workflow retries
Cron that runs once per cluster Leader-elected cron from cron expressions or @every, stored so every worker agrees
More than one worker Worker registration, heartbeats and reassignment of jobs from workers that stop heartbeating, plus a Kubernetes Lease-based cluster store in cluster/k8s
Big jobs next to small ones Resource-aware admission: jobs declare CPU, memory, disk or custom resources and workers only take what fits
Large inputs and outputs An artifact plane that tracks objects in Trove and stages them to a local cache
Visibility OpenTelemetry spans and labelled metrics for job attempts, cron, workflows, the durable runtime, the outbox and the artifact sweeper, plus lifecycle hooks for audit logs and Relay webhooks
An admin surface REST routes for jobs, workflow runs, the DLQ and crons, the Dispatch Wire Protocol over WebSocket, and Go, TypeScript and Python clients

The architecture guide shows how the dispatcher, the engine, the worker pool and the stores fit together.

Background jobs

A job definition is a name, a typed handler and options. Register it with the engine, then enqueue it by name:

var ResizeImage = job.NewDefinition("resize_image",
	func(ctx context.Context, in ResizeInput) error {
		if in.Width <= 0 {
			return fmt.Errorf("bad width %d: %w", in.Width, dispatch.ErrPermanent)
		}
		return resize(ctx, in)
	},
)

engine.Register(eng, ResizeImage)

_, err := engine.Enqueue(ctx, eng, "resize_image", ResizeInput{Width: 640},
	job.WithQueue("media"),
	job.WithMaxRetries(5),
	job.WithTimeout(2*time.Minute),
	job.WithPriority(10),
	job.WithRunAt(time.Now().Add(time.Hour)),
)

Pass the queue, retries, timeout and priority to Enqueue. job.NewDefinition accepts the same options, but Enqueue doesn't read them from the definition today, so set them per call. Unless you say otherwise, your job gets 3 retries, the default queue and a 5 minute timeout. When it runs out of retries, or returns an error wrapping dispatch.ErrPermanent, it moves to the dead letter queue. Workers only poll the queues you give dispatch.WithQueues, and engine.WithQueueConfig adds per-queue rate limits and concurrency.

Workflows

Dispatch has two workflow models. You can run both in one engine, and which one you pick mostly comes down to how long your process lives and whether something outside it has to talk to it while it runs.

Checkpointed workflows (workflow package) are ordinary Go functions made of named steps. Each completed step is recorded, so if your process dies partway through, the next engine start resumes the run and skips the steps that already finished. You also get Parallel, Sleep, WaitForEvent, WaitForAll, WaitForAny, StepWithCompensation for sagas, child workflows and versioned definitions with NewWorkflowV. Failed runs can be replayed from a chosen step.

Durable workflows (durable and durable/runtime) replay a deterministic handler against recorded history, in the style of Temporal. You schedule activities, timers and signal receives as futures, and the runtime gives you signals, queries, selectors, async activity completion, child workflows, cancellation, execution timeouts, continue-as-new and whole-workflow retry policies. Turn it on with engine.WithDurableWorkflows(...). It needs a store that implements durable.Store, which today means store/memory or store/postgres. Workflow code has to stay deterministic, so keep network calls, clocks and randomness in activities. The examples/ directory has a runnable program for each feature.

Cron

err := engine.RegisterCron(ctx, eng, &cron.Definition[Report]{
	Name:     "nightly-report",
	Schedule: "0 2 * * *",
	JobName:  "build_report",
	Payload:  Report{Kind: "daily"},
})

Each tick enqueues an ordinary job, so your cron runs get the same retries, DLQ and middleware as everything else. One worker holds cron leadership at a time. Registering the same name again changes nothing, so you can call this on every start without worrying about duplicate schedules piling up in the store.

Stores

store.Store combines the job, workflow, cron, DLQ, event, cluster and artifact stores, and every backend implements all of them.

Package Backend Built from Durable runtime
store/memory In memory, for development and tests memory.New() yes
store/postgres PostgreSQL postgres.New(*grove.DB) yes
store/sqlite SQLite, for a single node sqlite.New(*grove.DB) no
store/mongo MongoDB mongo.New(*grove.DB) no
store/redis Redis redis.New(*kv.Store) no

You hand the SQL and MongoDB stores a Grove database, and the Redis store a Grove KV store. The persistent stores have a Migrate method that creates their tables or collections. store/storetest holds the conformance suites if you want to write your own. See the store docs for each backend.

Use it with Forge

The github.com/xraph/dispatch/extension package plugs Dispatch into a Forge app. It depends on the Grove extension, picks up the *grove.DB it provides (Postgres, SQLite or MongoDB), runs the store migrations on start, builds the engine and provides *engine.Engine through DI. It also mounts the REST admin API under /dispatch/v1.

import (
	"github.com/xraph/dispatch/cron"
	"github.com/xraph/dispatch/engine"
	dispatchext "github.com/xraph/dispatch/extension"
	"github.com/xraph/forge"
	_ "github.com/xraph/grove/drivers/pgdriver"
	groveext "github.com/xraph/grove/extension"
)

jobs := dispatchext.New(dispatchext.WithConcurrency(8))

app := forge.NewApp(forge.AppConfig{
	Name:        "accounts",
	HTTPAddress: ":8080",
	Extensions: []forge.Extension{
		groveext.New(groveext.WithDSN("postgres", "postgres://localhost:5432/accounts?sslmode=disable")),
		jobs,
	},
})

err := app.RegisterHook(forge.PhaseAfterRegister, func(ctx context.Context, _ forge.App) error {
	eng := jobs.Engine()
	engine.Register(eng, SendWelcome)

	return engine.RegisterCron(ctx, eng, &cron.Definition[Signup]{
		Name:     "nightly-digest",
		Schedule: "0 2 * * *",
		JobName:  "send_welcome",
		Payload:  Signup{Email: "team@example.com"},
	})
}, forge.LifecycleHookOptions{Name: "register-jobs"})

err = app.Router().POST("/signup", func(ctx forge.Context) error {
	eng, err := forge.Inject[*engine.Engine](app.Container())
	if err != nil {
		return err
	}

	j, err := engine.Enqueue(ctx.Context(), eng, "send_welcome", Signup{Email: "ada@example.com"})
	if err != nil {
		return err
	}

	return ctx.JSON(http.StatusAccepted, map[string]string{"job_id": j.ID.String()})
})

jobs.Engine() is nil until the extension has registered, which is why the job registration sits in a PhaseAfterRegister hook. The admin routes (/dispatch/v1/jobs, /dispatch/v1/workflows/runs, /dispatch/v1/dlq, /dispatch/v1/crons, /dispatch/v1/stats and their actions) authenticate through the Forge auth extension and authorize with Warden by default. Pass WithRemoteSecurity to bring your own authenticator, or WithDisableRoutes() to mount nothing.

Configuration

The extension reads extensions.dispatch, then falls back to a top-level dispatch key.

extensions:
  dispatch:
    base_path: /dispatch
    grove_database: primary
    resources:
      enabled: true
      cpu_overcommit: 1.0
      memory_fraction: 0.8
Key Option What it does
base_path WithBasePath(path) Prefix for the REST routes, /dispatch by default
disable_routes WithDisableRoutes() Mounts no HTTP routes
disable_migrate WithDisableMigrate() Skips the store migrations on start
grove_database WithGroveDatabase(name) Uses a named *grove.DB from the container
grove_kv, key_prefix WithGroveKV(name), WithKVKeyPrefix(p) Uses a Grove KV store with the Redis backend
enable_dwp, dwp_base_path WithDWP(...) Turns on the Dispatch Wire Protocol over WebSocket
resources.enabled, cpu_overcommit, memory_fraction, explicit, custom_keys WithResources(), WithCPUOvercommit, WithMemoryFraction, WithExplicitCapacity Resource-aware admission against detected worker capacity
artifacts.enabled, trove_store, bucket, retention, cache.dir, cache.budget WithArtifacts(store), WithArtifactCacheDir, WithArtifactCacheBudget The artifact plane over a Trove store
durable.enabled, namespace, queue, build_id, concurrency The history-based durable runtime
security.installation_id, policy_tenant, providers WithOperatorSecurity(cfg) Which auth providers and Warden resource the admin routes use

Worker tuning is code-only: WithConcurrency, WithQueues, WithPollInterval, WithHeartbeatInterval, WithStaleJobThreshold and the WithCron* options. A dispatch: block nested under the extension key parses, but nothing reads it, so set those from Go. WithMiddleware, WithBackoff and WithExtension add job middleware, a retry strategy and lifecycle hooks. The Forge extension guide has more.

Examples

Every directory under examples/ is a runnable program for one feature of the durable runtime, built with the rest of the module:

Example What it shows
durable-signals Start a workflow with a signal and wait for it
durable-queries Read workflow state without changing it
durable-children Child workflows
durable-selectors Wait on whichever future finishes first
durable-async Activities completed from outside the worker
durable-timeouts Activity and execution timeouts
durable-cancellation, durable-workflow-cancellation Cancelling activities and whole workflows
durable-continuation Continue-as-new
durable-workflow-retry Whole-workflow retry policies

Run one with GOWORK=off go run ./examples/durable-signals. A larger resource-aware example lives in _examples/resources, which the Go tool skips in ./....

Documentation

Topic Docs
Install and run a first job Getting started, full example
Pick a store PostgreSQL, SQLite, MongoDB, Redis, memory
Handle failures Dead letter queue, errors
Watch it run Observability, admin API, DWP
Test your jobs Testing guide
Call it from other languages Go, TypeScript, Python clients

The exported Go API is on pkg.go.dev. The docs source is in docs/, with design notes in docs/architecture/.

FAQ

How do I run background jobs in Go without Redis or a separate server?

Use Dispatch with the PostgreSQL or SQLite store. Your queue lives in the database you already back up, the workers run inside your own process, and there's nothing new to deploy. Run more copies of your service and they share the work.

Does Dispatch retry failed jobs?

Yes. Each job retries up to its MaxRetries (3 by default) with exponential backoff and jitter, then lands in the dead letter queue, where you can replay it. Return an error that wraps dispatch.ErrPermanent when a retry can't help.

How is Dispatch different from Temporal?

Temporal is a cluster you run and talk to over the network. Dispatch is a Go library that keeps its state in your database. Its durable runtime borrows the same ideas (deterministic replay, activities, signals, queries, child workflows), and its checkpointed workflows are a simpler option for step-by-step processes.

Do cron jobs run once when I have several instances?

Yes. Cron entries live in the store, and only the instance holding cron leadership enqueues a tick. If it stops, another one takes over.

Can I use Dispatch without Forge?

Yes. The quick start above doesn't touch Forge. You only need it for the extension package.

Contributing

Issues and focused pull requests are welcome on GitHub. Use GOWORK=off on every Go command so you see the module graph CI sees:

git clone https://github.com/xraph/dispatch.git
cd dispatch
GOWORK=off go build ./...
make test-race
make lint
GOWORK=off go test -race -tags integration ./store/postgres/... ./store/redis/... ./extension/contract/...

The integration tests start containers and fail without Docker, and CI never runs them, so run them yourself when you touch a store. Some PostgreSQL tests also need a throwaway server in DISPATCH_READ_TEST_DSN or DISPATCH_LIFECYCLE_TEST_DSN and skip without one. Skip make build, make run and make dev: they point at a cmd/dispatch that doesn't exist. AGENTS.md explains the qualification/ modules, every test tier and the release workflow. Commits follow Conventional Commits.

License

MIT. See LICENSE.

Built by Rex Raphael and contributors, as part of the Forge forgery stack.

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

Constants

This section is empty.

Variables

View Source
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

type Context = context.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.

func NewEntity

func NewEntity() Entity

NewEntity returns an Entity with timestamps set to now.

type ID

type ID = id.ID

ID is the primary identifier type for all Dispatch entities.

type Option

type Option func(*Dispatcher) error

Option configures a Dispatcher.

func WithConcurrency

func WithConcurrency(n int) Option

WithConcurrency sets the maximum number of concurrent job processors.

func WithCronLeaderTTL

func WithCronLeaderTTL(d time.Duration) Option

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

func WithCronLockTTL(d time.Duration) Option

WithCronLockTTL sets the per-entry distributed lock TTL fired around each cron entry execution. Default 30s.

func WithCronRefreshInterval

func WithCronRefreshInterval(d time.Duration) Option

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

func WithCronStoreCallTimeout(d time.Duration) Option

WithCronStoreCallTimeout caps a single cron-scheduler store roundtrip (GetLeader, RenewLeadership, ListCrons, Acquire/Release lock, Update entry). Default 5s; negative disables (test-only).

func WithCronTickInterval

func WithCronTickInterval(d time.Duration) Option

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

func WithHeartbeatInterval(d time.Duration) Option

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

func WithLogger(l log.Logger) Option

WithLogger sets the structured logger for the dispatcher.

func WithMaxPollInterval

func WithMaxPollInterval(d time.Duration) Option

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

func WithPollInterval(d time.Duration) Option

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

func WithQueues(queues []string) Option

WithQueues sets the queues the dispatcher will poll.

func WithStaleJobThreshold

func WithStaleJobThreshold(d time.Duration) Option

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

func WithStore(s Storer) Option

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

func WithWorkerStoreCallTimeout(d time.Duration) Option

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 Prefix

type Prefix = id.Prefix

Prefix identifies the entity type encoded in a TypeID.

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.
job
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.
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.

Jump to

Keyboard shortcuts

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