Documentation
¶
Overview ¶
Package queue provides a durable, distributed task queue with pluggable backends (in-memory, Redis), priority scheduling, CPU-aware and goroutine-pool-aware dispatch, crash recovery, and consumer concurrency control.
Architecture ¶
- Task: a unit of work with ID, priority, payload, status, and metadata.
- Queue: pluggable backend (memory, Redis) that stores and dispatches tasks.
- Scheduler: manages a worker pool, pulls tasks from the queue, and executes them with optional CPU-based or goroutine-count-based throttling.
Backends ¶
- memory/ — in-process priority queue (single-node, fast, no persistence)
- redis/ — Redis-backed distributed queue (multi-node, persistent)
Scheduling modes ¶
- GoroutinePool: fixed worker count (default)
- CPUAdaptive: worker count scales with CPU usage (more workers when CPU is idle, fewer when busy)
Persistence & recovery ¶
When a persistent backend (Redis or DB) is used, tasks survive process restarts. On startup, the scheduler recovers pending/running tasks and re-queues them.
Index ¶
- Constants
- Variables
- func DecodePayload[T any](task *Task) (T, error)
- func EncodePayload(v any) (json.RawMessage, error)
- type CapacityScheduler
- func (s *CapacityScheduler) AppendLog(ctx context.Context, entry *TaskLogEntry) error
- func (s *CapacityScheduler) ListLogs(ctx context.Context, taskID string, limit int) ([]*TaskLogEntry, error)
- func (s *CapacityScheduler) Position(ctx context.Context, taskID string) (int, error)
- func (s *CapacityScheduler) Start() error
- func (s *CapacityScheduler) Stats() CapacitySchedulerStats
- func (s *CapacityScheduler) Stop() error
- func (s *CapacityScheduler) UpdateProgress(ctx context.Context, taskID string, progress int) error
- type CapacitySchedulerConfig
- type CapacitySchedulerMetrics
- type CapacitySchedulerStats
- type Handler
- type Queue
- type QueueStats
- type RichHandler
- type Scheduler
- type SchedulerConfig
- type SchedulerMetrics
- type SchedulerStats
- type SchedulingMode
- type SchedulingStrategy
- type Task
- type TaskContext
- type TaskLogEntry
- type TaskResult
- type TaskStatus
Constants ¶
const ( LogLevelInfo = "info" LogLevelWarn = "warn" LogLevelError = "error" )
LogLevel constants for TaskLogEntry.
Variables ¶
var ( // ErrQueueEmpty is returned when no tasks are available. ErrQueueEmpty = errors.New("queue: empty") // ErrQueueClosed is returned when the queue is closed. ErrQueueClosed = errors.New("queue: closed") // ErrTaskNotFound is returned when a task ID does not exist. ErrTaskNotFound = errors.New("queue: task not found") // ErrDuplicateTask is returned when a task ID already exists. ErrDuplicateTask = errors.New("queue: duplicate task") )
Sentinel errors.
Functions ¶
func DecodePayload ¶
DecodePayload unmarshals Task.Payload into a value.
func EncodePayload ¶
func EncodePayload(v any) (json.RawMessage, error)
EncodePayload marshals a value to JSON for Task.Payload.
Types ¶
type CapacityScheduler ¶
type CapacityScheduler struct {
// contains filtered or unexported fields
}
CapacityScheduler is a scheduler that enforces a total capacity limit on concurrently running tasks, supports job grouping (same JobID runs sequentially), priority-based scheduling, aging, and optional preemption.
It is designed for the scenario: "limited computing power, large number of async jobs — how to queue, preempt resources, and schedule by priority."
func NewCapacityScheduler ¶
func NewCapacityScheduler(cfg CapacitySchedulerConfig) (*CapacityScheduler, error)
NewCapacityScheduler creates a capacity-aware scheduler.
func (*CapacityScheduler) AppendLog ¶
func (s *CapacityScheduler) AppendLog(ctx context.Context, entry *TaskLogEntry) error
AppendLog appends an execution log entry for a task.
func (*CapacityScheduler) ListLogs ¶
func (s *CapacityScheduler) ListLogs(ctx context.Context, taskID string, limit int) ([]*TaskLogEntry, error)
ListLogs returns execution log entries for a task, newest first. limit of 0 means no limit (backend may apply its own cap).
func (*CapacityScheduler) Position ¶
Position returns the queue position of a pending task (0 = next to dispatch). Returns -1 if the task is not pending. This checks the scheduler's internal pending list first, then falls back to the backend.
func (*CapacityScheduler) Start ¶
func (s *CapacityScheduler) Start() error
Start launches the scheduler. Recovers pending tasks from the backend, then starts worker goroutines and the dispatch loop.
func (*CapacityScheduler) Stats ¶
func (s *CapacityScheduler) Stats() CapacitySchedulerStats
Stats returns a snapshot of scheduler statistics.
func (*CapacityScheduler) Stop ¶
func (s *CapacityScheduler) Stop() error
Stop gracefully shuts down the scheduler.
func (*CapacityScheduler) UpdateProgress ¶
UpdateProgress updates the execution progress of a running task (0-100). This is typically called from inside a RichHandler via TaskContext, but can also be called externally.
type CapacitySchedulerConfig ¶
type CapacitySchedulerConfig struct {
// Queue is the backend queue (memory or Redis).
Queue Queue
// Handler processes each task. If RichHandler is also set, RichHandler
// takes precedence (it receives a TaskContext for progress + logging).
Handler Handler
// RichHandler is an optional handler that receives a TaskContext for
// progress reporting and execution logging. If set, it takes precedence
// over Handler.
RichHandler RichHandler
// Capacity is the maximum total weight of concurrently running tasks.
// A task's Weight represents its resource cost. If Weight is 0, it
// counts as 1. The scheduler will not dispatch a task if doing so
// would exceed Capacity. Default: 0 (unlimited).
Capacity int
// WorkerCount is the maximum number of concurrent worker goroutines.
// Default: runtime.NumCPU().
WorkerCount int
// Strategy is the scheduling strategy.
// Default: StrategyPriority.
Strategy SchedulingStrategy
// AgingThreshold is the wait duration after which a task's priority
// is boosted to prevent starvation. Default: 30s.
// Set to 0 to disable aging.
AgingThreshold time.Duration
// DequeueTimeout is how long Dequeue blocks waiting for tasks.
// Default: 500ms.
DequeueTimeout time.Duration
// EnablePreemption allows high-priority tasks to preempt
// lower-priority running tasks when capacity is full.
// Only effective with StrategyPreemptive. Default: false.
EnablePreemption bool
// WorkerPool is an optional external worker pool.
WorkerPool *pool.WorkerPool
// OnTaskStart is called when a task begins executing.
OnTaskStart func(task *Task)
// OnTaskComplete is called when a task finishes.
OnTaskComplete func(task *Task, err error)
// OnPreempt is called when a task is preempted.
OnPreempt func(task *Task)
// OnRecover is called after crash recovery.
OnRecover func(count int)
}
CapacitySchedulerConfig configures the capacity-aware scheduler.
type CapacitySchedulerMetrics ¶
type CapacitySchedulerMetrics struct {
Dispatched atomic.Int64
Succeeded atomic.Int64
Failed atomic.Int64
Retried atomic.Int64
Preempted atomic.Int64
Recovered atomic.Int64
CapacityFull atomic.Int64
}
CapacitySchedulerMetrics holds atomic counters.
type CapacitySchedulerStats ¶
type CapacitySchedulerStats struct {
Pending int `json:"pending"`
Running int `json:"running"`
ActiveJobs int `json:"active_jobs"`
UsedWeight int `json:"used_weight"`
Capacity int `json:"capacity"`
Workers int `json:"workers"`
Strategy string `json:"strategy"`
Dispatched int64 `json:"dispatched"`
Succeeded int64 `json:"succeeded"`
Failed int64 `json:"failed"`
Retried int64 `json:"retried"`
Preempted int64 `json:"preempted"`
Recovered int64 `json:"recovered"`
CapacityFull int64 `json:"capacity_full"`
}
CapacitySchedulerStats is a point-in-time snapshot.
type Handler ¶
Handler processes a task. The implementation is responsible for unmarshalling the payload and performing the work.
type Queue ¶
type Queue interface {
// Enqueue adds a task to the queue. Returns ErrDuplicateTask if the
// task ID already exists.
Enqueue(ctx context.Context, task *Task) error
// Dequeue removes and returns the highest-priority pending task.
// Returns ErrQueueEmpty if no tasks are available. Blocks up to
// timeout if non-zero.
Dequeue(ctx context.Context, timeout time.Duration) (*Task, error)
// Ack marks a task as completed (success or failure).
Ack(ctx context.Context, taskID string, status TaskStatus, errMsg string) error
// Requeue moves a task back to pending status.
Requeue(ctx context.Context, taskID string) error
// Get retrieves a task by ID.
Get(ctx context.Context, taskID string) (*Task, error)
// Cancel removes a pending task or marks a running task as canceled.
Cancel(ctx context.Context, taskID string) error
// Position returns the queue position of a pending task (0 = next to
// dispatch). Returns -1 if the task is not in the pending queue.
Position(ctx context.Context, taskID string) (int, error)
// UpdateProgress updates the execution progress of a running task.
// progress should be 0-100.
UpdateProgress(ctx context.Context, taskID string, progress int) error
// AppendLog appends an execution log entry for a task.
AppendLog(ctx context.Context, entry *TaskLogEntry) error
// ListLogs returns execution log entries for a task, newest first.
// limit of 0 means no limit.
ListLogs(ctx context.Context, taskID string, limit int) ([]*TaskLogEntry, error)
// Recover returns all pending and interrupted (running) tasks for
// restart recovery. Running tasks are reset to pending.
Recover(ctx context.Context) ([]*Task, error)
// Stats returns current queue statistics.
Stats(ctx context.Context) (QueueStats, error)
// Close releases all backend resources.
Close() error
// Name returns the queue name.
Name() string
}
Queue is the backend interface for storing and dispatching tasks. Implementations must be safe for concurrent use.
type QueueStats ¶
type QueueStats struct {
Queue string `json:"queue"`
Pending int64 `json:"pending"`
Running int64 `json:"running"`
Completed int64 `json:"completed"`
Failed int64 `json:"failed"`
Total int64 `json:"total"`
}
QueueStats is a snapshot of queue metrics.
type RichHandler ¶
type RichHandler func(tctx TaskContext, task *Task) error
RichHandler is a handler that receives a TaskContext for progress and logging. Schedulers that support rich handlers will call this instead of the plain Handler when configured.
type Scheduler ¶
type Scheduler struct {
// contains filtered or unexported fields
}
Scheduler pulls tasks from a Queue and dispatches them to workers.
func NewScheduler ¶
func NewScheduler(cfg SchedulerConfig) (*Scheduler, error)
NewScheduler creates a scheduler with the given config.
func (*Scheduler) Start ¶
Start launches worker goroutines and begins processing tasks. If a persistent backend is used, pending tasks are recovered first.
func (*Scheduler) Stats ¶
func (s *Scheduler) Stats() SchedulerStats
Stats returns current scheduler statistics.
type SchedulerConfig ¶
type SchedulerConfig struct {
// Queue is the backend queue (memory or Redis).
Queue Queue
// Handler processes each task.
Handler Handler
// WorkerCount is the initial/fixed number of workers.
// Default: runtime.NumCPU().
WorkerCount int
// Mode is the scheduling mode.
// Default: ModeFixedPool.
Mode SchedulingMode
// MinWorkers is the minimum workers in CPUAdaptive mode.
// Default: 1.
MinWorkers int
// MaxWorkers is the maximum workers in CPUAdaptive mode.
// Default: WorkerCount * 4.
MaxWorkers int
// CPUHighThreshold is the CPU usage % above which workers are reduced.
// Default: 80.
CPUHighThreshold float64
// CPULowThreshold is the CPU usage % below which workers are added.
// Default: 30.
CPULowThreshold float64
// CPUCheckInterval is how often CPU usage is evaluated.
// Default: 5s.
CPUCheckInterval time.Duration
// DequeueTimeout is how long Dequeue blocks waiting for tasks.
// Default: 1s.
DequeueTimeout time.Duration
// WorkerPool is an optional external worker pool. If set, the
// scheduler dispatches tasks to this pool instead of spawning its
// own worker goroutines.
WorkerPool *pool.WorkerPool
// OnTaskStart is called when a task begins executing.
OnTaskStart func(task *Task)
// OnTaskComplete is called when a task finishes.
OnTaskComplete func(task *Task, err error)
// OnRecover is called after crash recovery with the number of recovered tasks.
OnRecover func(count int)
}
SchedulerConfig configures the scheduler.
type SchedulerMetrics ¶
type SchedulerMetrics struct {
Dequeued atomic.Int64
Succeeded atomic.Int64
Failed atomic.Int64
Retried atomic.Int64
Recovered atomic.Int64
}
SchedulerMetrics holds atomic counters.
type SchedulerStats ¶
type SchedulerStats struct {
Workers int `json:"workers"`
Running int `json:"running"`
Dequeued int64 `json:"dequeued"`
Succeeded int64 `json:"succeeded"`
Failed int64 `json:"failed"`
Retried int64 `json:"retried"`
Recovered int64 `json:"recovered"`
CPUUsage float64 `json:"cpu_usage"`
}
SchedulerStats is a point-in-time snapshot.
type SchedulingMode ¶
type SchedulingMode int
SchedulingMode controls how the scheduler manages worker concurrency.
const ( // ModeFixedPool uses a fixed number of workers (default). ModeFixedPool SchedulingMode = iota // ModeCPUAdaptive scales workers based on CPU usage. ModeCPUAdaptive )
type SchedulingStrategy ¶
type SchedulingStrategy int
SchedulingStrategy determines how pending tasks are ordered for dispatch.
const ( // StrategyFIFO dispatches tasks in submission order (oldest first). // Simple and fair, but ignores priority and weight. StrategyFIFO SchedulingStrategy = iota // StrategyPriority dispatches highest-priority tasks first. // Tasks with the same priority are ordered by submission time. // May cause starvation of low-priority tasks. StrategyPriority // StrategyWeightedFair dispatches tasks using a weighted fair queuing // algorithm. Each priority level gets a proportional share of // execution slots. Combined with aging to prevent starvation. StrategyWeightedFair // StrategyPreemptive is like StrategyPriority but also supports // preemption: when capacity is full and a high-priority task arrives, // a lower-priority running task is preempted (paused and re-queued). StrategyPreemptive )
func (SchedulingStrategy) String ¶
func (s SchedulingStrategy) String() string
String returns a human-readable strategy name.
type Task ¶
type Task struct {
ID string `json:"id"`
Queue string `json:"queue"`
Kind string `json:"kind,omitempty"`
JobID string `json:"job_id,omitempty"`
Priority int `json:"priority"`
Weight int `json:"weight,omitempty"`
Payload json.RawMessage `json:"payload"`
Status TaskStatus `json:"status"`
Progress int `json:"progress"`
RetryCount int `json:"retry_count"`
MaxRetries int `json:"max_retries"`
ErrorMsg string `json:"error_msg,omitempty"`
SubmitTime time.Time `json:"submit_time"`
StartedAt *time.Time `json:"started_at,omitempty"`
FinishedAt *time.Time `json:"finished_at,omitempty"`
WorkerID string `json:"worker_id,omitempty"`
// Preemptible indicates whether this task can be preempted by a
// higher-priority task when capacity is full. Default: true.
Preemptible bool `json:"preemptible,omitempty"`
}
Task is a unit of work in the queue.
type TaskContext ¶
type TaskContext interface {
context.Context
Task() *Task
SetProgress(progress int) error
Log(level string, message string) error
}
TaskContext is passed to handlers when using a scheduler that supports progress reporting and execution logging. It wraps the base context and task, and provides methods to update progress and append log entries to the queue backend.
Usage:
func myHandler(tctx queue.TaskContext, task *queue.Task) error {
tctx.SetProgress(10)
tctx.Log(queue.LogLevelInfo, "starting work")
// ... do work ...
tctx.SetProgress(100)
return nil
}
type TaskLogEntry ¶
type TaskLogEntry struct {
TaskID string `json:"task_id"`
Level string `json:"level"` // "info", "warn", "error"
Message string `json:"message"`
Timestamp time.Time `json:"timestamp"`
}
TaskLogEntry is a single execution log line for a task.
type TaskResult ¶
type TaskResult struct {
TaskID string
Status TaskStatus
Error error
Elapsed time.Duration
}
TaskResult holds the outcome of a task execution.
type TaskStatus ¶
type TaskStatus string
TaskStatus is the lifecycle state of a task.
const ( StatusPending TaskStatus = "pending" StatusRunning TaskStatus = "running" StatusSuccess TaskStatus = "success" StatusFailed TaskStatus = "failed" StatusCanceled TaskStatus = "canceled" StatusRetry TaskStatus = "retry" )
func (TaskStatus) IsTerminal ¶
func (s TaskStatus) IsTerminal() bool
IsTerminal reports whether the status is a final state.
func (TaskStatus) String ¶
func (s TaskStatus) String() string
String returns the status as a string.