Jobs
Background Jobs
Register typed background jobs, choose a backend, enqueue work, and inspect job state.
Define jobs
GoLazy jobs are typed Go structs with JSON payloads. A job has a stable kind
and a Work method:
package importwhatsapp
import (
"context"
"sample_app/app/jobs/basejob"
)
type Job struct {
basejob.BaseJob
ImportID int64 `json:"import_id"`
}
func (*Job) Kind() string { return "imports.whatsapp" }
func (j *Job) Work(ctx context.Context) error {
// Load services from ctx and process j.ImportID.
return nil
}
Put the application base job in app/jobs/basejob/basejob.go:
package basejob
import "golazy.dev/lazyjobs"
type BaseJob struct {
lazyjobs.BaseJob
}
The app base package keeps concrete job packages from importing the registry package and avoids Go import cycles.
Register jobs
Register jobs from app/jobs/jobs.go:
package jobs
import (
"golazy.dev/lazyjobs"
"sample_app/app/jobs/importwhatsapp"
)
func DefinedJobs(runner *lazyjobs.JobRunner) {
runner.MustRegister(&importwhatsapp.Job{})
}
Wire the registry into lazyapp.Config.Jobs:
lazyapp.New(lazyapp.Config{
Name: "sample_app",
Drawer: Draw,
Public: app.Public,
Views: app.Views,
Dependencies: Dependencies,
Jobs: lazyapp.Jobs(lazyjobs.Config{
Define: jobs.DefinedJobs,
}),
})
Jobs runs after Dependencies with the dependency-initialized app context.
When no backend is passed, GoLazy uses the bundled in-memory backend.
Backend interface
Durability is behind lazyjobs.Backend:
type Backend interface {
Insert(context.Context, lazyjobs.InsertParams) (lazyjobs.Record, error)
Claim(context.Context, lazyjobs.ClaimParams) (lazyjobs.Record, bool, error)
Complete(context.Context, int64) error
Retry(context.Context, lazyjobs.RetryParams) error
Discard(context.Context, lazyjobs.DiscardParams) error
List(context.Context, lazyjobs.ListOptions) ([]lazyjobs.Record, error)
Stats(context.Context) (lazyjobs.Stats, error)
}
Insert stores pending work. Claim must atomically select one due job from
the requested queues and move it to running. Complete, Retry, and
Discard record runner decisions after Work returns or panics. List and
Stats power operational views.
The backend owns persistence and concurrency. The runner owns decoding,
calling Work, retry-delay decisions, and state-transition calls.
Implement a backend
A backend should:
- Store
Kind,Queue, JSONPayload,State, attempts,RunAt, timestamps, andLastError. - Default empty queues to
lazyjobs.DefaultQueue. - Claim only
pendingorretryingrecords whoseRunAtis due. - Make claim atomic so two workers cannot run the same record.
- Preserve payload bytes exactly enough for registered job decoding.
- Return newest recent records from
List. - Return aggregate counts by state, kind, and queue from
Stats.
If the backend owns network connections or goroutines, implement Close() error;
the runner calls it during shutdown when present.
Built-in backends
golazy.dev/lazyjobs/inmemoryjobs is the default in-process backend. It is
useful for development, tests, and apps where queued work does not need to
survive process restarts.
PostgreSQL apps can use golazy.dev/pg/pgjobs. Include
pgjobs.Migrations() in the migration catalog and build the job config from
context after dependencies initialize. Read
PostgreSQL.
Enqueue work
Resolve the runner from the request or service context:
runner, ok := lazyjobs.RunnerFromContext(ctx)
if !ok {
return errors.New("jobs are not configured")
}
_, err := runner.Enqueue(ctx, &importwhatsapp.Job{ImportID: importID})
return err
The runner serializes the job payload to JSON, stores it through the backend, and starts in-process workers with the application.
Use EnqueueIn or EnqueueAt for delayed work.
Inspect jobs
When jobs are configured, GoLazy registers read-only GET /jobs on the app
control plane. It returns runner status, job definitions, state counts, and
recent jobs. In lazy development, the GoLazy panel proxies that endpoint and
shows the same data in the Jobs tab.
Production builds still follow normal control-plane exposure rules: use
CONTROL_PLANE_ADDR when job state should be available outside application
routes.