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

jobs

package
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2026 License: AGPL-3.0 Imports: 13 Imported by: 0

Documentation

Overview

Package jobs is the DB-backed job queue: enqueue, workers with retry and exponential backoff, and a coarse cron scheduler.

Status lifecycle: queued → running → done, or on error back to queued with run_at = now + min(2^attempts, 300)s, and dead once attempts reaches max_attempts. A handler that returns an error after the worker's ctx was cancelled (shutdown) is requeued without counting an attempt. "failed" is a virtual status for Stats and List: queued jobs that already failed at least once (awaiting retry). attempts counts failed runs only.

Index

Constants

This section is empty.

Variables

View Source
var DefaultRetention = Retention{Done: 7 * 24 * time.Hour, Dead: 30 * 24 * time.Hour, Keyed: 90 * 24 * time.Hour}

DefaultRetention keeps done jobs 7 days, dead jobs 30 days and jobs with an idempotency key 90 days.

View Source
var ErrNotFound = errors.New("jobs: not found")

ErrNotFound is returned by Retry when no retryable job has the given id.

Functions

func AttemptFromContext

func AttemptFromContext(ctx context.Context) (attempt, max int)

AttemptFromContext returns, inside a job handler, the 1-based number of the attempt being run and the job's max attempts (attempt == max is the final attempt). Outside a handler it returns 0, 0.

func ContextWithAttempt

func ContextWithAttempt(ctx context.Context, attempt, max int) context.Context

ContextWithAttempt returns ctx carrying attempt/max for AttemptFromContext. The worker sets it for every handler run; tests and code that invoke a handler directly can set it themselves.

func Migrations

func Migrations() fs.FS

Migrations returns the jobs table migrations ("migrations/NNNN_x.sql").

Types

type Handler

type Handler func(ctx context.Context, payload json.RawMessage) error

Handler processes one job payload. A non-nil error schedules a retry.

type Job

type Job struct {
	ID             string          `json:"id"`
	Kind           string          `json:"kind"`
	Payload        json.RawMessage `json:"payload"`
	Status         string          `json:"status"`
	RunAt          string          `json:"run_at"`
	Attempts       int             `json:"attempts"`
	MaxAttempts    int             `json:"max_attempts"`
	LastError      string          `json:"last_error"`
	LockedBy       string          `json:"locked_by"`
	LockedAt       string          `json:"locked_at"`
	CreatedAt      string          `json:"created_at"`
	UpdatedAt      string          `json:"updated_at"`
	IdempotencyKey string          `json:"idempotency_key"`
}

Job is a row of the jobs table.

type Option

type Option func(*enqueueOptions)

Option configures Enqueue.

func Idempotency

func Idempotency(key string) Option

Idempotency dedupes on key: enqueueing an existing key returns the existing job's id instead of inserting a new job.

func RunAt

func RunAt(t time.Time) Option

RunAt delays the job until t.

type Queue

type Queue struct {
	// contains filtered or unexported fields
}

Queue is the job queue. Register handlers and crons before Start.

func New

func New(d *sql.DB, log *slog.Logger) *Queue

New returns a queue on d. The jobs migrations must already be applied.

func (*Queue) Count

func (q *Queue) Count(ctx context.Context) (int, error)

Count returns the number of rows in the jobs table.

func (*Queue) Cron

func (q *Queue) Cron(name string, every time.Duration, fn func(ctx context.Context) error)

Cron runs fn roughly every `every`, checked on the scheduler tick (30s), so intervals shorter than the tick are rounded up to it. The first tick after Start runs every cron. Register crons before Start.

func (*Queue) Enqueue

func (q *Queue) Enqueue(ctx context.Context, kind string, payload any, opts ...Option) (string, error)

Enqueue stores a job and returns its id. payload is marshalled to JSON.

Enqueue writes on its own pool connection: never call it inside db.InTx (it would wait on the transaction's lock for busy_timeout and fail). Use EnqueueTx inside a transaction, or call Enqueue after commit.

func (*Queue) EnqueueTx

func (q *Queue) EnqueueTx(ctx context.Context, tx *sql.Tx, kind string, payload any, opts ...Option) (string, error)

EnqueueTx is Enqueue inside tx: the job exists only if tx commits.

func (*Queue) List

func (q *Queue) List(ctx context.Context, status string, limit int) ([]Job, error)

List returns up to limit jobs, newest first. status "" means all; "failed" means queued jobs awaiting retry; "queued" includes those too.

func (*Queue) Prune

func (q *Queue) Prune(ctx context.Context, now time.Time, r Retention) (int64, error)

Prune deletes done and dead jobs older than r (relative to now) and returns how many rows it removed. Queued, running and failed jobs are never touched.

func (*Queue) Register

func (q *Queue) Register(kind string, h Handler, maxAttempts int)

Register binds a handler to kind. maxAttempts <= 0 means the default (5).

func (*Queue) Retry

func (q *Queue) Retry(ctx context.Context, id string) error

Retry requeues a queued, failed or dead job to run now with attempts reset.

func (*Queue) Start

func (q *Queue) Start(ctx context.Context, workers int)

Start requeues jobs abandoned by a previous process (see recoverStale), then launches workers goroutines and the cron scheduler, which repeats the recovery on every tick. It returns immediately; cancel ctx to stop and call Wait to block until every goroutine has exited.

func (*Queue) Stats

func (q *Queue) Stats(ctx context.Context) (Stats, error)

Stats returns job counts by status.

func (*Queue) Wait

func (q *Queue) Wait()

Wait blocks until all goroutines started by Start have exited.

type Retention

type Retention struct {
	Done, Dead, Keyed time.Duration
}

Retention says how long finished jobs are kept, by their last update (updated_at, the time they finished). Keyed applies to jobs with an idempotency key: they guard against duplicates (e.g. a webhook event id), so they are kept for the longer of Keyed and their status window.

type Stats

type Stats struct {
	Queued  int `json:"queued"`
	Running int `json:"running"`
	Failed  int `json:"failed"`
	Dead    int `json:"dead"`
	Done    int `json:"done"`
}

Stats counts jobs by status. Queued excludes jobs awaiting retry, which are counted in Failed.

Jump to

Keyboard shortcuts

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