Oban for Go
Background job processing for Go, standard library only.
An Oban/Sidekiq-style background job system for Go built on nothing but the standard library. An Oban engine runs named queues at configured concurrency, resolves workers by name from a Registry, and executes each job with a per-attempt timeout. Failures retry with exponential backoff and jitter until they succeed or exhaust their attempts, at which point they are discarded. The engine also de-duplicates unique jobs, schedules periodic work with cron expressions, wraps every attempt in middleware/telemetry, and shuts down gracefully by draining in-flight jobs. It is deterministic and testable: time flows through an injectable clock, backoff jitter is seedable, and cron scheduling is pure. A complete in-memory Store ships in the box, and the Store interface documents exactly what a database-backed implementation must guarantee.
Install
$ go get github.com/malcolmston/obanQuick start
import "github.com/malcolmston/oban"
engine, _ := oban.New(oban.Config{
Store: oban.NewInMemoryStore(),
Queues: map[string]int{"default": 5, "mailers": 2},
})
// Register a worker by name.
engine.RegisterFunc("email", func(ctx context.Context, job *oban.Job) error {
var args struct{ To string `json:"to"` }
if err := job.UnmarshalArgs(&args); err != nil {
return err
}
return sendEmail(ctx, args.To)
})
_ = engine.Start(ctx)
job, _ := oban.NewJob("email", map[string]string{"to": "ada@example.com"},
oban.WithQueue("mailers"), oban.WithMaxAttempts(5))
_, _, _ = engine.Enqueue(ctx, job)Features
Obanengine — polls named queues at configured concurrency, built withNew(Config{...})and driven byStart/Stop- Jobs as data — a
Jobcarries queue, JSONArgs, attempts, priority, schedule and error history; build one withNewJob - Named workers — implement
Worker(Perform(ctx, *Job) error) or register a func withRegisterFuncin aRegistry - Retries with backoff — the
Backoffinterface, withExponentialBackoffgrowing the delay and adding seedable jitter up to a cap - Discard on exhaustion — jobs that use up
MaxAttemptstransition to discarded and fire theErrorHandler - Cron scheduling — declare
Periodicjobs against a parsed 5-fieldSchedule;Schedule.Nextis a pure function - Unique jobs —
WithUnique(key, period)de-duplicates by queue + worker + key over a time window - Middleware & telemetry — wrap every attempt with a
Middlewarechain, withTelemetryinstalled innermost to time the worker - Pluggable persistence — a complete
InMemoryStoreships in the box; implementStore(SELECT ... FOR UPDATE SKIP LOCKED semantics) for a database - Deterministic & testable — the engine owns time through an injectable
Clock, so scheduling, backoff and uniqueness test without real sleeps - Graceful shutdown —
Stopstops fetching and drains in-flight work, honouring the context deadline - Zero dependencies — pure Go standard library, no cgo, nothing to audit but the toolchain