Background jobs, checkpointed multi-step workflows, fan-out/fan-in,
crash recovery, and an embedded real-time dashboard — in a single import.
Review the Guarantees & Production Readiness page for the AS-IS warranty disclaimer, high-risk-use exclusion, execution semantics, and production tuning contract.
Up and running in a few lines
package main
import (
"context"
"fmt"
jobs "github.com/jdziat/simple-durable-jobs/v4"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
func main() {
db, _ := gorm.Open(sqlite.Open("jobs.db?_journal_mode=WAL&_busy_timeout=5000&_txlock=immediate"), &gorm.Config{})
storage := jobs.NewGormStorage(db)
_ = storage.Migrate(context.Background())
queue := jobs.New(storage)
// Register a typed handler.
queue.Register("greet", func(ctx context.Context, name string) error {
fmt.Println("Hello,", name)
return nil
})
// Enqueue work and start processing.
queue.Enqueue(context.Background(), "greet", "world")
jobs.NewWorker(queue).Start(context.Background())
}Everything you need to run jobs reliably
Multi-step workflows with automatic checkpointing. If a workflow fails, it resumes from the last successful step instead of starting over.
Spawn parallel sub-jobs, wait for results, and aggregate. Supports fail-fast, collect-all, and threshold strategies.
Jobs persist to a database and survive crashes. A stale-lock reaper automatically reclaims jobs stuck on dead workers.
A real-time monitoring dashboard with stats, historical charts, live event streaming, and job management — mounted into your own HTTP server.
Cron expressions plus daily, weekly, and interval-based scheduling with a built-in scheduler.
Minimal boilerplate, type-safe handlers, and a clean facade. Import one package and start processing jobs.
Enqueue inside your own database transaction so business rows and the jobs that act on them commit — or roll back — together. No dual-write race.
Send, wait, check, and drain durable signals to coordinate jobs with each other and with the outside world — buffered, FIFO, and consumed exactly-once. (Handlers themselves are at-least-once; keep side effects idempotent.)
Jobs that exhaust retries (or hit a non-retryable error) carry explicit dead-letter metadata you can list, count, inspect, and requeue.
Throttle execution with per-queue token buckets and an optional fleet-wide limiter, and cap concurrency fleet-wide or per key — without burning retry attempts.
Opt into an exporter for queue depth, throughput, wait and run latency, attempts, failures, and retries — scrapeable for alerting and capacity planning.
Plug in a codec to transparently encrypt job arguments, results, checkpoints, and error text (last_error / dead-letter reason) at rest, with key rotation, at the storage boundary. (opt-in; encrypts the listed fields at the application/storage boundary — it is not whole-database encryption and is not a compliance attestation.)
A real-time dashboard, embedded in your app
