Introduction
Composable durable execution engine for Go.
Dispatch is a Go library for background job processing, durable workflow orchestration, cron scheduling, and distributed worker coordination. Import it into your Go program — no separate binary, no DSL, no YAML.
Dispatch is a library — not a service. You bring your own database, HTTP server, and process lifecycle. Dispatch provides the execution plumbing.
What it does
- Background jobs — Define typed handlers, enqueue with priority, retry with configurable backoff.
- Durable workflows — Multi-step Go functions that checkpoint progress and resume after failure.
- Distributed cron — Leader-elected cron scheduling with per-tenant support and runtime enable/disable.
- Dead letter queue — Jobs that exhaust retries land in the DLQ for inspection, replay, or purge.
- Distributed workers — Worker registration, heartbeats, leader election, and automatic work stealing.
- Middleware — Composable chain for logging, tracing, metrics, panic recovery, and Forge scope injection.
- Extension hooks — Opt-in lifecycle interfaces for every job, workflow, cron, and shutdown event.
- OpenTelemetry — Built-in metrics and tracing via the
observabilityandmiddlewarepackages. - Relay integration — Emit typed webhook events at every lifecycle point via
relay_hook. - Pluggable storage — Memory, PostgreSQL (pgx/v5), Bun ORM, SQLite, Redis. Implement
store.Storefor anything else.
Design philosophy
Library, not framework. Dispatch is a set of Go packages you import. You control the main function, the HTTP server, and the process lifecycle.
Interfaces over implementations. Every subsystem defines a Go interface. Swap any storage backend with a single type change.
Composable stores. Each subsystem has its own store interface. The aggregate store.Store composes them all. One backend satisfies every subsystem.
Workflows as ordinary Go functions. No YAML, no protobuf, no external scheduler. A workflow is a function; a step is a closure.
TypeID everywhere. All entities use type-prefixed, K-sortable, UUIDv7-based identifiers. Passing a CronID where a JobID is expected is a compile error.
Quick look
package main
import (
"context"
"log"
"github.com/xraph/dispatch"
"github.com/xraph/dispatch/engine"
"github.com/xraph/dispatch/job"
"github.com/xraph/dispatch/store/memory"
)
type EmailInput struct {
To string `json:"to"`
Subject string `json:"subject"`
}
var SendEmail = job.NewDefinition("send_email",
func(ctx context.Context, input EmailInput) error {
log.Printf("sending to %s: %s", input.To, input.Subject)
return nil
},
)
func main() {
ctx := context.Background()
d, err := dispatch.New(dispatch.WithStore(memory.New()))
if err != nil {
log.Fatal(err)
}
eng := engine.Build(d)
engine.Register(eng, SendEmail)
d.Start(ctx)
defer d.Stop(ctx)
engine.Enqueue(ctx, eng, SendEmail, EmailInput{
To: "user@example.com",
Subject: "Welcome!",
})
}