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

jobs

package
v0.16.0 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2026 License: AGPL-3.0 Imports: 15 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"`
	ParkedReason   string          `json:"parked_reason" doc:"Non-empty while the job is held for a disabled or missing handler"`
}

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 ParkedCounts added in v0.15.0

type ParkedCounts map[string]int

ParkedCounts reports queued jobs that are parked, by kind.

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) CronNames added in v0.16.0

func (q *Queue) CronNames() []string

CronNames returns the registered cron names in registration order.

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) Get added in v0.15.0

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

Get returns the job with id, or ErrNotFound.

func (*Queue) InFlight added in v0.15.0

func (q *Queue) InFlight(ctx context.Context, key string) (string, bool, error)

InFlight reports whether the job with idempotency key is still queued or running, returning its id when so. Done and dead jobs are not in flight.

func (*Queue) Kinds added in v0.15.0

func (q *Queue) Kinds() []string

Kinds returns the registered handler kinds, sorted.

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) Park added in v0.15.0

func (q *Queue) Park(ctx context.Context, kind, reason string) (int64, error)

Park marks every queued job of kind as parked with reason. Running, done and dead jobs are not touched. It returns how many rows changed.

func (*Queue) Parked added in v0.15.0

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

Parked counts parked queued jobs by kind.

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) QueuedByKind added in v0.16.0

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

QueuedByKind counts queued, unparked jobs by kind (whatever their handler state), so a module page can show the work waiting for it.

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) RunOne added in v0.15.0

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

RunOne claims and runs one due job on the caller's goroutine, reporting whether a job ran. It is for tests and one-shot tools that drive the queue without Start; a live worker process should not call it.

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) Unpark added in v0.15.0

func (q *Queue) Unpark(ctx context.Context, kind string) (int64, error)

Unpark releases parked jobs of kind back to the queue, due now, with their payload, attempts and idempotency key unchanged.

func (*Queue) Unregistered added in v0.15.0

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

Unregistered counts queued, unparked jobs whose kind has no handler in this process. They are never claimed; the count tells operators that a disabled or missing module left work waiting.

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"`
	Parked  int `json:"parked" doc:"Queued jobs held for a disabled or missing handler; counted in neither queued nor failed"`
}

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