horizon

package
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Jul 16, 2026 License: MIT Imports: 14 Imported by: 0

README

Horizon Plugin for Nimbus (Laravel Horizon 1:1 style)

Horizon provides a queue dashboard and code-driven configuration for Nimbus, inspired by Laravel Horizon. It surfaces queue metrics, failed job management (list/forget/retry), and optional dashboard authorization.

Features

  • Dashboard at /horizon: dispatched/processed/failed counts, per-queue stats, failed jobs list
  • Failed jobs (when Redis is configured): list, forget one, forget all, retry — persisted in Redis
  • Dashboard authorization: optional Gate so only allowed users can access Horizon in production
  • Code-driven config: environments, supervisors, balance strategy, tries, timeout, backoff (for future worker scaling)
  • Job interfaces: optional Tags() []string and Silenced() bool on jobs for Laravel-style tagging and silencing
  • JSON API: /horizon/api/metrics, /horizon/api/workloads (live Redis queue depths), /horizon/api/failed (list), POST /horizon/failed/:id/forget, /horizon/failed/forget-all, /horizon/failed/:id/retry
  • Live Redis workloads (when RedisURL is set): pending / delayed / processing / in-flight counts per queue on the Pending page — same Redis keys as queue.RedisAdapter. Use HORIZON_QUEUES (comma-separated) to always probe named queues even before traffic.

Installation

import "github.com/CodeSyncr/nimbus/plugins/horizon"

// Basic (in-memory stats only)
app.Use(horizon.New())

// With Redis failed store and dashboard gate (Laravel-style)
app.Use(horizon.NewWithOptions(horizon.Options{
    RedisURL: os.Getenv("REDIS_URL"),
    Gate: func(c *http.Context) bool {
        // e.g. only allow admin users
        return c.User() != nil && c.User().Email == "admin@example.com"
    },
}))

Ensure queue.Boot() is called (e.g. in bin/server.go) and that jobs are registered. Use Redis as the queue driver to persist failed jobs.

Configuration

Variable Description Default
HORIZON_ENABLED Allow dashboard in production false
HORIZON_PATH Dashboard base path /horizon
REDIS_URL Used by Options.RedisURL for failed store + live workloads
HORIZON_QUEUES Extra queue names to show Redis depths for (comma-separated)

Without RedisURL, the dashboard still shows in-memory stats but failed jobs are not persisted (no list/forget/retry) and Redis workload rows are hidden.

Horizon Config (workers)

Use Config for Laravel-style worker configuration (environments, supervisors, tries, timeout, backoff). Pass it when creating the plugin or use DefaultConfig():

cfg := horizon.DefaultConfig()
// Customize: cfg.Environments["production"].Supervisors["supervisor-1"].MaxProcesses = 20
app.Use(horizon.NewWithOptions(horizon.Options{ Config: &cfg, RedisURL: os.Getenv("REDIS_URL") }))

See config.go for SupervisorConfig (Balance: "auto"|"simple"|"false", Processes, MinProcesses, MaxProcesses, Tries, Timeout, Backoff).

CLI (Laravel-style)

From your app root:

  • nimbus horizon forget [id] — forget a failed job by ID
  • nimbus horizon forget --all — forget all failed jobs
  • nimbus horizon clear [--queue=name] — clear pending jobs from a queue (default: default)

These delegate to go run . horizon forget ... / go run . horizon clear .... Your app must handle these in main.go so that the Horizon plugin and queue are booted, then call the failed store or queue clear. Example for horizon forget:

if len(os.Args) >= 3 && os.Args[1] == "horizon" && os.Args[2] == "forget" {
    app := bin.Boot()
    if err := app.Boot(); err != nil { os.Exit(1) }
    store := queue.GetFailedJobStore()
    if store == nil { fmt.Println("Failed job store not configured"); os.Exit(1) }
    ctx := context.Background()
    if len(os.Args) > 3 && os.Args[3] == "--all" {
        _ = store.ForgetAll(ctx)
        fmt.Println("All failed jobs forgotten.")
    } else if len(os.Args) > 3 {
        _ = store.Forget(ctx, os.Args[3])
        fmt.Println("Forgotten:", os.Args[3])
    }
    return
}

Similar handling for horizon clear (use queue.GetGlobal().Adapter() and clear the queue if the adapter supports it, or document that clear is best-effort).

Job tags and silenced

Implement on your job type:

  • Tags() []string — for Horizon dashboard (e.g. return []string{"mail", "user:" + strconv.FormatUint(uint64(j.UserID), 10)})
  • Silenced() bool — return true to hide from completed jobs list (config or per-job)

Metrics API

  • GET /horizon — HTML dashboard
  • GET /horizon/metrics — JSON snapshot of stats
  • GET /horizon/failed — JSON list of failed jobs (when Redis store configured)
  • POST /horizon/failed/:id/forget — forget one
  • POST /horizon/failed/forget-all — forget all
  • POST /horizon/failed/:id/retry — re-queue and forget

All routes respect the authorization Gate when set.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// Environments holds per-environment supervisor sets (e.g. "production", "local").
	Environments map[string]EnvironmentConfig
	// Defaults are merged into each supervisor.
	Defaults SupervisorDefaults
	// Waits is per-queue wait threshold in seconds for long-wait notifications (e.g. "redis:default" => 60).
	Waits map[string]int
	// Silenced job class names to hide from completed list.
	Silenced []string
	// SilencedTags hide jobs with any of these tags.
	SilencedTags []string
}

Config is the top-level Horizon configuration.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns a Laravel-like default for local development.

func (*Config) CurrentEnvironment

func (c *Config) CurrentEnvironment() string

CurrentEnvironment returns the environment name (e.g. from APP_ENV).

func (*Config) MergeSupervisor

func (c *Config) MergeSupervisor(s SupervisorConfig) SupervisorConfig

MergeSupervisor merges Defaults into a supervisor config.

func (*Config) SupervisorsForCurrentEnv

func (c *Config) SupervisorsForCurrentEnv() map[string]SupervisorConfig

SupervisorsForCurrentEnv returns supervisor configs for the current environment. If no match, tries "*" then "local".

type EnvironmentConfig

type EnvironmentConfig struct {
	Supervisors map[string]SupervisorConfig
}

EnvironmentConfig holds supervisors for one environment (e.g. APP_ENV=production).

type Options

type Options struct {
	// Config is the Horizon worker config (environments, supervisors). If nil, DefaultConfig() is used.
	Config *Config
	// Gate authorizes dashboard access. If nil, dashboard is allowed only when APP_ENV is not production or HORIZON_ENABLED=true.
	Gate func(*http.Context) bool
	// RedisURL enables failed job store and pause/continue state. If empty, failed jobs are not persisted.
	RedisURL string
}

Options configures the Horizon plugin (Laravel-style).

type Plugin

type Plugin struct {
	nimbus.BasePlugin
	// contains filtered or unexported fields
}

Plugin integrates a Horizon-style dashboard and optional failed job store.

func New

func New() *Plugin

New creates a new Horizon plugin with default options.

func NewWithOptions

func NewWithOptions(opts Options) *Plugin

NewWithOptions creates a Horizon plugin with the given options.

func (*Plugin) Boot

func (p *Plugin) Boot(app *nimbus.App) error

Boot is a no-op for now (reserved for future enhancements).

func (*Plugin) Config

func (p *Plugin) Config() *Config

Config returns the Horizon config (for CLI/workers).

func (*Plugin) DefaultConfig

func (p *Plugin) DefaultConfig() map[string]any

DefaultConfig returns default configuration for Horizon.

func (*Plugin) FailedStore

func (p *Plugin) FailedStore() queue.FailedJobStore

FailedStore returns the failed job store if Redis was configured.

func (*Plugin) Gate

func (p *Plugin) Gate() func(*http.Context) bool

Gate returns the authorization gate (for routes).

func (*Plugin) Redis

func (p *Plugin) Redis() *redis.Client

Redis returns the Redis client if RedisURL was set (for pause/continue, etc.).

func (*Plugin) Register

func (p *Plugin) Register(app *nimbus.App) error

Register registers the plugin views, queue observer, and optional failed job store.

func (*Plugin) RegisterRoutes

func (p *Plugin) RegisterRoutes(r *router.Router)

RegisterRoutes mounts the Horizon dashboard routes (Laravel Horizon style).

func (*Plugin) ViewsFS

func (p *Plugin) ViewsFS() fs.FS

ViewsFS returns the embedded Horizon views for the view engine.

type QueueStats

type QueueStats struct {
	Name           string
	Dispatched     int64
	Processed      int64
	Failed         int64
	Retried        int64
	Reclaimed      int64
	LastDispatched *time.Time
	LastProcessed  *time.Time
}

QueueStats holds metrics for a specific queue.

type Stats

type Stats struct {
	StartedAt time.Time

	TotalDispatched int64
	TotalProcessed  int64
	TotalFailed     int64
	TotalRetried    int64
	TotalReclaimed  int64

	PerQueue map[string]*QueueStats
	// contains filtered or unexported fields
}

Stats holds in-memory queue statistics for the Horizon dashboard.

func NewStats

func NewStats() *Stats

NewStats creates a new Stats instance.

func (*Stats) JobDispatched

func (s *Stats) JobDispatched(payload *queue.JobPayload)

JobDispatched implements queue.Observer.

func (*Stats) JobProcessed

func (s *Stats) JobProcessed(payload *queue.JobPayload, err error)

JobProcessed implements queue.Observer.

func (*Stats) JobRetried

func (s *Stats) JobRetried(payload *queue.JobPayload, nextDelay time.Duration)

JobRetried implements queue.RetryObserver.

func (*Stats) JobsReclaimed

func (s *Stats) JobsReclaimed(queueName string, count int)

JobsReclaimed implements queue.ReclaimObserver.

type SupervisorConfig

type SupervisorConfig struct {
	// Connection name (e.g. "redis").
	Connection string
	// Queue names this supervisor processes.
	Queue []string
	// Balance: "auto", "simple", or "false" (strict order).
	Balance string
	// Auto/simple: total process count. Simple splits evenly across queues.
	Processes int
	// Auto: min processes per queue.
	MinProcesses int
	// Auto/false: max total processes.
	MaxProcesses int
	// Auto: max processes to add/remove per balance step.
	BalanceMaxShift int
	// Auto: seconds between balance steps.
	BalanceCooldown int
	// Max job attempts (0 = unlimited). Job $tries overrides if set.
	Tries int
	// Job timeout in seconds (force kill worker).
	Timeout int
	// Backoff in seconds before retry, or nil for default.
	Backoff []int
	// Force run in maintenance mode when true.
	Force bool
}

SupervisorConfig defines a group of workers (Laravel "supervisor").

func (*SupervisorConfig) BackoffDuration

func (s *SupervisorConfig) BackoffDuration(attempt int) time.Duration

BackoffDuration returns the delay before retry for the given attempt (1-based).

type SupervisorDefaults

type SupervisorDefaults struct {
	Connection string
	Timeout    int
	Tries      int
	Backoff    []int
}

SupervisorDefaults are merged into each supervisor.

Jump to

Keyboard shortcuts

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