Documentation
¶
Overview ¶
Package dhara is a PostgreSQL-backed task queue for Go.
A Client enqueues tasks; a Worker claims and executes them. Postgres is the only dependency: task state, locking, retries and execution logs all live in the database.
Enqueue a task:
client := dhara.NewClient(pool) res, err := client.Enqueue(ctx, "send_email", payload)
Run a worker:
w := dhara.NewWorker(pool, dhara.WithMaxWorkers(10))
w.RegisterHandler("send_email", handleEmail)
if err := w.Start(ctx); err != nil { // blocks until ctx is cancelled
log.Fatal(err)
}
Delivery is at-least-once: a handler can run more than once after a retry, a stale-heartbeat reap, or a shutdown, so handlers must be idempotent. See the repository README for design notes and operational guidance.
Index ¶
- Constants
- Variables
- type Client
- func (c *Client) CancelTask(ctx context.Context, id string) (*Task, error)
- func (c *Client) Enqueue(ctx context.Context, taskType string, payload any, opts ...EnqueueOption) (*EnqueueResult, error)
- func (c *Client) EnqueueTx(ctx context.Context, tx pgx.Tx, taskType string, payload any, ...) (*EnqueueResult, error)
- func (c *Client) GetTask(ctx context.Context, id string) (*Task, error)
- func (c *Client) Insert(ctx context.Context, params InsertParams) (*EnqueueResult, error)
- func (c *Client) InsertTx(ctx context.Context, tx pgx.Tx, params InsertParams) (*EnqueueResult, error)
- func (c *Client) ListTasks(ctx context.Context, filter TaskListFilter) ([]Task, int, error)
- func (c *Client) Metrics() MetricsSnapshot
- func (c *Client) MetricsHandler() http.Handler
- func (c *Client) QueueMetrics(ctx context.Context) (*QueueMetrics, error)
- func (c *Client) RetryTask(ctx context.Context, id string) (*Task, error)
- type EnqueueOption
- type EnqueueResult
- type HandlerFunc
- type InsertParams
- type MetricsSnapshot
- type MigrationResult
- func Migrate(ctx context.Context, pool *pgxpool.Pool) (MigrationResult, error)
- func MigrateDown(ctx context.Context, pool *pgxpool.Pool, steps int) (MigrationResult, error)
- func MigrateDownWith(ctx context.Context, pool *pgxpool.Pool, fsys fs.FS, steps int) (MigrationResult, error)
- func MigrateWith(ctx context.Context, pool *pgxpool.Pool, fsys fs.FS) (MigrationResult, error)
- type QueueMetrics
- type Task
- type TaskListFilter
- type TaskListResult
- type TaskStatus
- type ValidationError
- type Worker
- type WorkerOption
- func WithBaseBackoff(d time.Duration) WorkerOption
- func WithHandlerTimeout(d time.Duration) WorkerOption
- func WithHeartbeatInterval(d time.Duration) WorkerOption
- func WithLogger(l *slog.Logger) WorkerOption
- func WithMaxBackoff(d time.Duration) WorkerOption
- func WithMaxWorkers(n int) WorkerOption
- func WithPollInterval(d time.Duration) WorkerOption
- func WithReaperInterval(d time.Duration) WorkerOption
- func WithShutdownTimeout(d time.Duration) WorkerOption
- func WithStuckThreshold(d time.Duration) WorkerOption
- func WithWorkerPrefix(prefix string) WorkerOption
Constants ¶
const ( DefaultPriority = 0 DefaultMaxRetries = 5 )
Defaults applied by Normalize when the caller leaves a field unset.
Variables ¶
var ( // ErrTaskConflict is returned when an idempotency key is reused with a // different payload. ErrTaskConflict = dharatype.ErrTaskConflict // ErrTaskNotFound is returned when no task matches the given ID. ErrTaskNotFound = dharatype.ErrTaskNotFound // ErrTaskNotAvailable is returned by Claim when no task is ready. ErrTaskNotAvailable = dharatype.ErrTaskNotAvailable // ErrTaskOwnershipLost is returned when a worker tries to update a task // the reaper has already requeued. ErrTaskOwnershipLost = dharatype.ErrTaskOwnershipLost // ErrTaskNotDead is returned by RetryTask for a task that is not DEAD. ErrTaskNotDead = dharatype.ErrTaskNotDead )
Errors returned by Client and Worker. Compare them with errors.Is.
var MigrationFS fs.FS = mustSub(migrationsRoot, "migrations")
MigrationFS exposes the migrations embedded in the library, rooted at the migrations directory (flat NNNNNN_name.up.sql / .down.sql files).
Functions ¶
This section is empty.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client enqueues and inspects tasks. It is safe for concurrent use, owns no goroutines, and keeps all state in Postgres.
func NewClient ¶
NewClient returns a Client backed by pool. The caller owns the pool; the library never closes it.
func (*Client) CancelTask ¶
CancelTask cancels a PENDING task. A RUNNING task is returned unchanged so the caller can reject it; terminal tasks are returned as-is. Returns ErrTaskNotFound if the ID does not exist.
func (*Client) Enqueue ¶
func (c *Client) Enqueue(ctx context.Context, taskType string, payload any, opts ...EnqueueOption) (*EnqueueResult, error)
Enqueue marshals payload and inserts a task of the given type, applying opts.
func (*Client) EnqueueTx ¶
func (c *Client) EnqueueTx(ctx context.Context, tx pgx.Tx, taskType string, payload any, opts ...EnqueueOption) (*EnqueueResult, error)
EnqueueTx marshals payload and inserts the task inside tx, applying opts.
func (*Client) Insert ¶
func (c *Client) Insert(ctx context.Context, params InsertParams) (*EnqueueResult, error)
Insert normalizes and validates params, then enqueues the task in its own transaction. Use InsertTx to join an existing transaction.
func (*Client) InsertTx ¶
func (c *Client) InsertTx(ctx context.Context, tx pgx.Tx, params InsertParams) (*EnqueueResult, error)
InsertTx enqueues the task inside tx. The caller owns the transaction: the task commits or rolls back with it.
func (*Client) ListTasks ¶
ListTasks returns the tasks matching filter and the total count before pagination.
func (*Client) Metrics ¶
func (c *Client) Metrics() MetricsSnapshot
Metrics returns a point-in-time read of this client's process-local counters. Only the enqueue and cancel counters are non-zero on a Client.
func (*Client) MetricsHandler ¶
MetricsHandler renders this client's process-local counters alongside the Postgres-derived queue gauges.
It always responds 200. When Postgres cannot be read, queue_metrics_up is 0 and the gauge series are omitted, so counters keep flowing during the outage you are trying to debug.
func (*Client) QueueMetrics ¶
func (c *Client) QueueMetrics(ctx context.Context) (*QueueMetrics, error)
QueueMetrics reads task counts by state from Postgres in a single query.
type EnqueueOption ¶
type EnqueueOption func(*InsertParams)
EnqueueOption customizes an Enqueue or EnqueueTx call.
func WithIdempotencyKey ¶
func WithIdempotencyKey(key string) EnqueueOption
WithIdempotencyKey makes submission replay-safe: the same (type, key) with the same payload returns the existing task. A different payload returns ErrTaskConflict.
func WithMaxRetries ¶
func WithMaxRetries(maxRetries int) EnqueueOption
WithMaxRetries sets the maximum number of attempts, including the first. Zero means one attempt and no retries. Valid range: 0..20.
func WithPriority ¶
func WithPriority(priority int) EnqueueOption
WithPriority sets claim order; higher values are claimed first. Valid range: 0..100.
func WithRunAt ¶
func WithRunAt(runAt time.Time) EnqueueOption
WithRunAt delays the task until runAt. It may not be more than 5 minutes in the past or more than 30 days in the future.
type EnqueueResult ¶
type EnqueueResult struct {
// Task is the created task, or the existing task on an idempotent replay.
Task *Task
// Duplicate is true if an existing task with the same
// (type, idempotency_key) was returned instead of inserting a new one.
Duplicate bool
}
EnqueueResult is returned by every insert method.
type HandlerFunc ¶
type HandlerFunc = dharatype.HandlerFunc
HandlerFunc is the signature of a task handler. Returning a non-nil error schedules a retry, or dead-letters the task once its attempts are exhausted.
type InsertParams ¶
type InsertParams struct {
Type string
Payload json.RawMessage
IdempotencyKey *string
Priority *int
MaxRetries *int
RunAt *time.Time
}
InsertParams is the shared input for creating a task. Pointer fields distinguish "not provided" (default applied) from an explicit zero value (e.g. MaxRetries: 0 means "never retry", while nil means the default 5).
func (*InsertParams) Normalize ¶
func (p *InsertParams) Normalize(now time.Time)
Normalize fills in defaults for unset fields: priority 0, max_retries 5, run_at now, payload {}.
type MetricsSnapshot ¶
type MetricsSnapshot struct {
TasksEnqueuedTotal int64
TasksCompletedTotal int64
TaskAttemptFailuresTotal int64
TasksRetriedTotal int64
TasksDeadTotal int64
TasksCanceledTotal int64
TasksReapedTotal int64
ReaperFailuresTotal int64
ClaimErrorsTotal int64
WorkersActive int64
WorkersInflight int64
}
MetricsSnapshot is a point-in-time read of one process's counters. The server and worker track disjoint sets: enqueue/cancel on the server, execution and reaper counters on the worker. Values reset on restart.
type MigrationResult ¶
type MigrationResult struct {
Versions []string // versions applied or rolled back in this run
Skipped int // versions already at the target state
}
MigrationResult reports what a migration run changed
func MigrateDown ¶
MigrateDown rolls back the given number of migrations using the embedded files. Each rollback runs the matching .down.sql
func MigrateDownWith ¶
func MigrateDownWith(ctx context.Context, pool *pgxpool.Pool, fsys fs.FS, steps int) (MigrationResult, error)
MigrateDownWith rolls back migrations from a custom FS.
func MigrateWith ¶
MigrateWith applies pending migrations from a custom FS (e.g. your own os.DirFS when you maintain your own migration files).
type QueueMetrics ¶
type QueueMetrics struct {
TasksPending int64
TasksRunning int64
TasksCompleted int64
TasksCanceled int64
TasksDead int64
TasksPendingReady int64
TasksPendingDelayed int64
}
QueueMetrics is a point-in-time snapshot of task counts by state.
type Task ¶
type Task struct {
ID uuid.UUID `json:"id"`
Type string `json:"type"`
Status TaskStatus `json:"status"`
Priority int `json:"priority"`
Attempts int `json:"attempts"`
MaxRetries int `json:"max_retries"`
RunAt time.Time `json:"run_at"`
IdempotencyKey *string `json:"idempotency_key,omitempty"`
StartedAt *time.Time `json:"started_at,omitempty"`
CompletedAt *time.Time `json:"completed_at,omitempty"`
LastError *string `json:"last_error,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Task is the public representation of a task. It intentionally omits the payload: the API never returns it, and callers that inserted the task already know what they put in.
type TaskListFilter ¶
type TaskListFilter = dharatype.TaskListFilter
TaskListFilter selects and paginates tasks for Client.ListTasks. The zero value lists all tasks with the default limit.
type TaskListResult ¶
TaskListResult is returned by list operations.
type TaskStatus ¶
type TaskStatus string
TaskStatus is the lifecycle state of a task.
const ( // TaskStatusPending means the task is waiting to be claimed. TaskStatusPending TaskStatus = "PENDING" // TaskStatusRunning means a worker has claimed and is executing the task. TaskStatusRunning TaskStatus = "RUNNING" // TaskStatusCompleted means the handler returned nil. TaskStatusCompleted TaskStatus = "COMPLETED" // TaskStatusCanceled means the task was canceled before completing. TaskStatusCanceled TaskStatus = "CANCELED" // TaskStatusDead means every attempt failed, or no handler was registered. TaskStatusDead TaskStatus = "DEAD" )
type ValidationError ¶
type ValidationError = dharatype.ValidationError
ValidationError wraps invalid InsertParams or enqueue options. Use errors.As to inspect the cause.
type Worker ¶
type Worker struct {
// contains filtered or unexported fields
}
Worker claims and executes tasks. Register handlers before calling Start.
func NewWorker ¶
func NewWorker(pool *pgxpool.Pool, opts ...WorkerOption) *Worker
NewWorker returns a Worker that claims tasks from pool. It starts no goroutines until Start is called.
func (*Worker) Metrics ¶
func (w *Worker) Metrics() MetricsSnapshot
Metrics returns a point-in-time read of this worker's process-local counters.
func (*Worker) MetricsHandler ¶
MetricsHandler renders the worker's process-local counters, gauges and execution-latency histogram. It never touches Postgres, so it stays scrapeable while the database is down.
func (*Worker) RegisterHandler ¶
func (w *Worker) RegisterHandler(taskType string, handler HandlerFunc)
RegisterHandler associates taskType with handler. It panics if called after Start.
func (*Worker) Start ¶
Start runs the worker pool and blocks until ctx is cancelled or Stop is called. In-flight handlers are cancelled, their outcomes persisted, and the call returns after the pool drains or WithShutdownTimeout elapses. It returns an error if the worker was already started or the drain timed out.
type WorkerOption ¶
type WorkerOption func(*workerConfig)
WorkerOption configures a Worker.
func WithBaseBackoff ¶
func WithBaseBackoff(d time.Duration) WorkerOption
WithBaseBackoff sets the initial retry delay. Default 1s.
func WithHandlerTimeout ¶
func WithHandlerTimeout(d time.Duration) WorkerOption
WithHandlerTimeout bounds a single handler invocation. Default 5m.
func WithHeartbeatInterval ¶
func WithHeartbeatInterval(d time.Duration) WorkerOption
WithHeartbeatInterval sets how often running tasks refresh their lock. Keep it well below WithStuckThreshold. Default 30s.
func WithLogger ¶
func WithLogger(l *slog.Logger) WorkerOption
WithLogger sets the structured logger. Default slog.Default().
func WithMaxBackoff ¶
func WithMaxBackoff(d time.Duration) WorkerOption
WithMaxBackoff caps the retry delay. Default 5m.
func WithMaxWorkers ¶
func WithMaxWorkers(n int) WorkerOption
WithMaxWorkers sets the number of concurrent handler goroutines. Default 5.
func WithPollInterval ¶
func WithPollInterval(d time.Duration) WorkerOption
WithPollInterval sets how long an idle worker waits before polling again. Default 1s.
func WithReaperInterval ¶
func WithReaperInterval(d time.Duration) WorkerOption
WithReaperInterval sets how often stale RUNNING tasks are scanned. Default 30s.
func WithShutdownTimeout ¶
func WithShutdownTimeout(d time.Duration) WorkerOption
WithShutdownTimeout bounds how long Start waits for in-flight handlers after its context is cancelled. Default 30s.
func WithStuckThreshold ¶
func WithStuckThreshold(d time.Duration) WorkerOption
WithStuckThreshold sets how long a task may go without a heartbeat before the reaper requeues it. Default 5m.
func WithWorkerPrefix ¶
func WithWorkerPrefix(prefix string) WorkerOption
WithWorkerPrefix sets the identity prefix for worker IDs and reaper logs. Default "dhara-worker".
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
migrate
command
|
|
|
server
command
|
|
|
worker
command
|
|
|
Package dharatype holds the shared data types used across the dhara library, the core engine, and the API server.
|
Package dharatype holds the shared data types used across the dhara library, the core engine, and the API server. |
|
internal
|
|
|
testutil
Package testutil provides shared helpers for dhara's test suites.
|
Package testutil provides shared helpers for dhara's test suites. |