跳转至正文

Horizon ​


Introduction ​

PrismGo Horizon provides queue runtime monitoring and supervisor management capabilities, corresponding to the core features of Laravel Horizon. It is built around github.com/prismgo/framework/queue events, workers, failed stores, and batch stores, and is responsible for queue runtime views, control commands, metrics aggregation, Dashboard API, and process heartbeats. It does not replace the queue driver itself.

Core process model:

ConceptDescription
MasterThe Horizon master process that loads the current environment's supervisor configuration and spawns child processes
SupervisorA configuration unit for a group of queue workers, declaring connection, queue, process count, retry, and timeout strategies
WorkerA real horizon:work child process that consumes queue tasks

Horizon's state is separate from queue messages. Queue messages are still managed by github.com/prismgo/framework/queue Redis, RabbitMQ, or other drivers; the Horizon Store only holds monitoring and control data.

Installation ​

Horizon is an independent optional module. Install it first:

bash
go get github.com/prismgo/horizon@latest

Then register its package provider in the Builder's extension layer:

go
import (
    "github.com/prismgo/framework/foundation"
    "github.com/prismgo/horizon"
)

app := foundation.Configure(basePath...).
    WithExtensionProviders(horizon.ServiceProvider{}).
    WithProviders(Providers()...).
    Create()

horizon.ServiceProvider runs Register and Boot after the framework defaults and before application providers; registration does not connect to Redis or RabbitMQ. Then use horizon:install to generate the configuration file and application-provider stub:

bash
go run ./ horizon:install

This command creates config/horizon.go and app/providers/horizon_service_provider.go only when they don't already exist; existing files are never overwritten. Add the generated app/providers.HorizonServiceProvider{} to the application-provider list returned by bootstrap/provider.go so it can mount Dashboard routes and application authorization. It has a different responsibility from the package extension provider above.

Dashboard assets are embedded in the binary, so no additional publish step is required.

Configuration ​

The configuration entry point is config/horizon.go. All configuration is registered under the horizon namespace and supports environment variable overrides.

Basic Configuration ​

Config KeyEnvironment VariableDefaultDescription
horizon.useHORIZON_STOREredisHorizon Store type. Use redis for production; memory for testing
horizon.pathHORIZON_PATHhorizonDashboard and internal API mount prefix, without leading/trailing slashes
horizon.connectionHORIZON_CONNECTIONdefaultRedis connection name used by the Horizon Redis Store, independent of queue/cache/session connections
horizon.prefixHORIZON_PREFIXprismgo_horizonKey prefix for the Horizon Store; should be distinct from business queue key prefixes
horizon.encodingHORIZON_ENCODINGemptyPayload Encoding name for Store records; empty inherits encoding.default (defaults to msgpack), set to json explicitly for debugging
horizon.heartbeat_ttl_secondsHORIZON_HEARTBEAT_TTL_SECONDS60Time window (seconds) within which a master/supervisor/worker heartbeat is considered alive. Processes that haven't refreshed their heartbeat beyond this window are marked as stale
horizon.fast_termination—falseWhether to allow a new master to start first during termination
horizon.memory_limit—128Default memory limit (MB) for Horizon workers
horizon.waits—emptyLong-wait threshold in seconds per connection:queue, used by the waits observability feature

Environment Selection ​

Horizon determines the current environment name using the following priority:

  1. horizon.environment — allows Horizon to independently override the application environment
  2. app.env — project-level configuration source
  3. APP_ENV environment variable — compatibility fallback
  4. Default value production

The environment name is used to select the corresponding supervisor configuration from horizon.environments. Environment matching supports Laravel Str::is style wildcards — for example, production-* matches production-web, production-worker, etc. Exact matches take priority over wildcard matches.

Supervisor Configuration ​

horizon.defaults is the baseline supervisor configuration shared across all environments; supervisors with the same name in horizon.environments.{env} override fields from defaults. Supervisors that only exist in the environment section are also parsed, allowing per-environment queue consumer groups to be added.

Configuration Fields ​

FieldTypeDefaultDescription
connectionstringrequiredQueue connection name; determines which queue connection the worker pulls tasks from
queuestring or []stringrequiredQueue name list; supports comma-separated strings or arrays. Whitespace is trimmed, duplicates are removed, and configuration order is preserved
balancestringfalseLoad balancing strategy: false, simple, or auto
min_processesint0Minimum number of workers for the supervisor
max_processesint0Maximum number of workers for the supervisor; must be >= min_processes
triesint0Maximum retry attempts for a single task; after reaching this limit, the task enters the failed store
timeoutint0Execution timeout in seconds for a single task
sleepint0Polling interval in seconds when the queue is empty
max_jobsint0Number of jobs after which the worker exits; 0 means no limit
max_timeint0Number of seconds after which the worker exits; 0 means no limit
retry_afterint0Time window in seconds before a reserved task becomes visible again; should always be greater than timeout to avoid concurrent duplicate consumption
backoffint or []intemptyFailure retry backoff in seconds; supports a single number, comma-separated string, or array
stop_when_emptyboolfalseWhether the worker exits when the queue is empty; suitable for one-time consumption scenarios
memoryint0Worker memory limit in MB (compatibility field; currently only parsed and displayed in the Go implementation)
niceint0Process priority (compatibility field; currently only parsed and displayed in the Go implementation)
auto_scaling_strategystringtimeStrategy used by auto balance: time (by estimated drain time) or size (by queue length)
balance_max_shiftint1Maximum number of workers that auto balance can move in a single scaling operation; limits scaling magnitude
balance_cooldownint3Minimum interval in seconds between two auto balance calculations; reduces scaling oscillation

Note: Only snake_case field names are supported. camelCase fields (such as maxProcesses, minProcesses, retryAfter, etc.) will cause a parse error with a message indicating the correct snake_case name.

Configuration Example ​

go
"defaults": map[string]interface{}{
    "supervisor-default": map[string]interface{}{
        "connection":    "redis",
        "queue":         []interface{}{"high", "default"},
        "balance":       "auto",
        "min_processes": 1,
        "max_processes": 4,
        "tries":         3,
        "timeout":       60,
        "retry_after":   90,
        "sleep":         3,
    },
},
"environments": map[string]interface{}{
    "production": map[string]interface{}{
        "supervisor-default": map[string]interface{}{
            "max_processes": 10,
        },
        "supervisor-notifications": map[string]interface{}{
            "connection":    "redis",
            "queue":         "notifications",
            "balance":       "false",
            "min_processes": 2,
            "max_processes": 2,
            "tries":         3,
            "timeout":       30,
        },
    },
},

Scenario Recommendations ​

ScenarioRecommendation
Local developmentmax_processes=1 to reduce debug noise from concurrency
Standard production queuesbalance=auto, min_processes=1, set max_processes based on throughput
High-priority notification/payment queuesSeparate supervisor with its own queue and max_processes
One-time compensation tasksstop_when_empty=true with a small max_time
Long-running tasksIncrease timeout and correspondingly increase retry_after

Balance Strategies ​

Horizon supports three load balancing strategies, corresponding to Laravel Horizon's balance configuration:

balance=false (default) ​

No automatic balancing. All configured queues are consumed by a single process pool, with the worker count adjusted between min_processes and max_processes based on queue backlog. Suitable for scenarios where each queue has a fixed process count or automatic scheduling is not needed.

balance=simple ​

Fixed allocation across configured queues. Each queue gets max_processes / len(queues) workers, with the result clamped to the [min_processes, max_processes] range. Suitable for simple fixed allocation across multiple queues.

balance=auto ​

Dynamically adjusts worker count between min_processes and max_processes based on queue workload. Constrained by balance_max_shift and balance_cooldown to prevent aggressive scaling or oscillation.

The workload calculation for auto balance is determined by auto_scaling_strategy:

StrategyCalculationUse Case
time (default)weight = ready_jobs × avg_runtimeWhen you care more about wait time; aligns with Laravel's default semantics
sizeweight = ready_jobsWhen you care more about backlog size

Running Horizon ​

Starting the Master ​

In production, the horizon master is typically started by a process manager:

bash
go run ./ horizon --environment=production

The --environment flag specifies the Horizon environment name; when empty, the actual runtime environment is determined by the loaded configuration. The master process loads the current environment's supervisor configuration and spawns supervisor child processes.

Starting a Worker ​

Workers are typically spawned automatically by supervisors, but can also be run manually:

bash
go run ./ horizon:work redis --queue=default --tries=3 --timeout=60

horizon:work supports the following options:

OptionTypeDefaultDescription
--namestringemptyWorker name
--queuestringdefaultQueue name(s) to listen on; comma-separated values are supported
--connectionpositional argemptyQueue connection name
--backoffstring0Backoff strategy
--retry-afterint90Job retry retention window (seconds)
--max-jobsint0Maximum jobs per worker; 0 means no limit
--max-timeint0Maximum runtime in seconds per worker; 0 means no limit
--forceboolfalseForce run flag
--memoryint0Memory limit (MB); 0 means no limit
--onceboolfalseProcess only one job and exit
--stop-when-emptyboolfalseStop when the queue is empty
--sleepint3Sleep seconds when queue is empty
--restint0Rest seconds between jobs
--supervisorstringemptyParent supervisor name
--timeoutint60Per-job timeout in seconds
--triesint1Maximum retry attempts
--jsonboolfalseJSON output mode
--environmentstringemptyHorizon environment name
--prefixstringemptyStore key prefix

Local Development Watcher ​

bash
go run ./ horizon:listen

horizon:listen is a local development helper that watches for file changes and restarts Horizon. It supports the following options:

OptionTypeDefaultDescription
--environmentstringlocalHorizon environment name
--pollint1000File polling interval (milliseconds)

Note: horizon:listen is intended for local development only and is not recommended for production.

Control Commands ​

Pausing & Resuming ​

Pause all supervisors:

bash
go run ./ horizon:pause

Resume all supervisors:

bash
go run ./ horizon:continue

Pause a specific supervisor:

bash
go run ./ horizon:pause-supervisor supervisor-default

Resume a specific supervisor:

bash
go run ./ horizon:continue-supervisor supervisor-default

Pausing and resuming are implemented through Store control flags and do not directly terminate processes. Paused workers will not consume new tasks, but tasks already in progress will continue to completion.

Graceful Termination ​

bash
go run ./ horizon:terminate
go run ./ horizon:terminate --wait

The --wait flag controls the termination wait strategy. Terminate requests take priority over pause; once written, they notify fresh master/supervisor processes to exit as soon as possible.

Diagnostic Commands ​

CommandDescription
horizon:statusDisplay the global runtime status snapshot, including status, pause/terminate flags, supervisor/worker counts, and stale counts
horizon:listList running master machines, showing Name, PID, Supervisors, Status
horizon:supervisorsList all supervisors with basic status
horizon:supervisor-status <name>Display detailed status for a specific supervisor, including Pools, Connection, Queues, etc.
horizon:timeout [environment]Display the maximum worker timeout in seconds for the specified environment; defaults to production
horizon:staleList all stale processes (master/supervisor/worker) whose heartbeat has exceeded the TTL; read-only, no writes

Maintenance Commands ​

CommandDescription
horizon:snapshotPersist queue length snapshots and current collector event-derived metrics to the Store
horizon:clear-metricsClear event-derived metrics from the Store and collector memory aggregation. Does not clear heartbeats, control flags, queue data, or failed records
horizon:clear <connection> --queue=<name> --forceClear a queue covered by the current environment's supervisor configuration. The --force flag is required
horizon:forget <id>Delete a specific failed job record
horizon:forget --allDelete all failed job records
horizon:purge --signal=SIGTERMPurge orphan worker processes that no longer belong to an active supervisor pool

Dashboard ​

The Dashboard path is controlled by horizon.path, defaulting to /horizon. The internal API uses read-only endpoints under the same prefix.

Route Registration ​

Call RegisterHTTPRoutes during application route registration:

go
horizon.RegisterHTTPRoutes(horizon.HTTPOptions{
    Manager: manager,
    Auth:    []gin.HandlerFunc{authMiddleware, permissionMiddleware},
})

HTTPOptions fields:

FieldTypeDescription
Manager*ManagerProvides Horizon static configuration and Store resolution; uses the default Manager when empty
Auth[]gin.HandlerFuncAuthentication middleware injected by the application, typically including login verification, permission loading, and horizon.view authorization

Authorization ​

The Dashboard and API share the horizon.view permission identifier. Currently, only the observation surface is exposed; write operations such as retry, pause, and terminate are not available through the API. For production, it is recommended to add authentication and IP allowlists for the /horizon path at the application routing layer.

HTTP API Reference ​

All API endpoints are read-only GET routes mounted under the /{horizon.path}/api prefix. Endpoints that support pagination accept page and page_size query parameters.

Global Status ​

GET /{horizon.path}/api/status

Returns a lightweight runtime status summary for the Dashboard landing page, including global status, queue lengths, and capability list.

Response example:

json
{
  "status": {
    "status": "running",
    "global_paused": false,
    "terminate_requested": false,
    "supervisor_count": 2,
    "worker_count": 8,
    "stale_supervisor_count": 0,
    "stale_worker_count": 0,
    "queue_count": 3,
    "jobs_per_minute": 150.5,
    "jobs_past_hour": 9030,
    "total_processed": 25000
  },
  "queue_lengths": { "captured_at": "...", "queues": [...] },
  "capabilities": {
    "batches": "supported",
    "high_value_detail": "supported",
    "queue_lengths": "supported",
    "event_metrics": "supported",
    "waits": "supported",
    "http_writes": "unsupported"
  }
}

Master List ​

GET /{horizon.path}/api/masters?page=1&page_size=25

Returns a list of running master processes, including process identity, heartbeat, goroutine, and memory metrics.

Supervisor List ​

GET /{horizon.path}/api/supervisors?page=1&page_size=25

Returns a list of supervisors, including name, status, worker count, connection, queues, and process pool state.

Worker List ​

GET /{horizon.path}/api/workers?page=1&page_size=25

Returns a list of workers, including process identity, status, configured queues, and resource metrics.

Stale Processes ​

GET /{horizon.path}/api/stale?page=1&page_size=25

Returns stale supervisors and workers whose heartbeat has exceeded the TTL.

Queue View ​

GET /{horizon.path}/api/queues?page=1&page_size=25

Returns an aggregated queue view combining queue lengths and event metrics to show throughput, wait times, and runtime information for each connection:queue.

Current Metrics ​

GET /{horizon.path}/api/metrics/current

Returns the current event metrics aggregated view. Defaults to the last 24 hours of data.

Query parameters:

ParameterTypeDescription
fromRFC3339Time range lower bound (window WindowEnd > From)
toRFC3339Time range upper bound (window WindowStart < To)
source_hoststringExact filter by host
source_environmentstringExact filter by Horizon environment
source_supervisorstringExact filter by supervisor
connectionstringExact filter by queue connection
queuestringExact filter by queue name
source_detailsboolInclude source shard details; omitted by default
summary_onlyboolReturn only queue aggregation summary
page / page_sizeintPagination parameters

The time range must not exceed 24 hours. from must be before to. All time parameters must include a timezone.

Metric Sources ​

GET /{horizon.path}/api/metrics/sources

Returns the source dimension list for event metrics, used for Dashboard drill-down. Supports the same filtering parameters as /metrics/current.

Metric History ​

GET /{horizon.path}/api/metrics/history/{kind}/{key}

Returns metric history data points for the specified dimension. Currently, kind only supports queue, and key uses the connection:queue format.

Query parameters are the same as /metrics/current.

High-Value Detail List ​

GET /{horizon.path}/api/high-value-detail

Returns a list of high-value diagnostic summaries for failed, poison, and slow_job types.

Query parameters:

ParameterTypeDescription
kindstringFilter by type: failed, poison, or slow_job
occurred_fromRFC3339Event occurrence time lower bound
occurred_toRFC3339Event occurrence time upper bound
page / page_sizeintPagination parameters

High-Value Detail ​

GET /{horizon.path}/api/high-value-detail/{id}

Returns the diagnostic summary detail for the specified ID. Returns 404 if not found.

Batch Summary List ​

GET /{horizon.path}/api/batches

Returns a list of batch safe summaries. Supports query or search parameters to search by ID or name.

Batch Detail ​

GET /{horizon.path}/api/batches/{id}

Returns the batch summary detail for the specified ID. Returns 404 if not found.

Observability Configuration ​

Horizon generates wait times, runtimes, failures, poison envelopes, batch summaries, and queue length metrics through queue events. Observability configuration is centralized under horizon.observability.

Presets ​

PresetDescription
fullDevelopment and debugging priority; retains full observability capabilities with 100% sampling
production_lightRetains core health, queue lengths, and queue-level aggregation; reduces per-job cost with 10% sampling
minimalRetains only core health and queue lengths; disables event_metrics and per-job details

Presets only provide default values; all explicit sub-fields override the preset. This allows you to start from production_light and selectively enable diagnostic capabilities.

Capability Switches ​

Config KeyEnvironment VariableDefault (full)Description
observability.presetHORIZON_OBSERVABILITY_PRESETfullObservability preset
observability.event_metricsHORIZON_OBSERVABILITY_EVENT_METRICStrueWhether to collect queue event-derived queue-level counters and runtime aggregation
observability.waitsHORIZON_OBSERVABILITY_WAITStrueWhether to retain queued wait time state and long wait events. Requires event_metrics=true and queued_waits_max>0
observability.batch_summariesHORIZON_OBSERVABILITY_BATCH_SUMMARIEStrueWhether to save batch read-only summaries
observability.process_healthHORIZON_OBSERVABILITY_PROCESS_HEALTHtrueWhether to enable heartbeat, status, and control flag core health capabilities
observability.queue_lengthsHORIZON_OBSERVABILITY_QUEUE_LENGTHStrueWhether to sample queue backend lengths
observability.queued_waits_maxHORIZON_OBSERVABILITY_QUEUED_WAITS_MAX10000Maximum number of per-job wait time states; 0 means no retention
observability.processing_spans_maxHORIZON_OBSERVABILITY_PROCESSING_SPANS_MAX10000Maximum number of processing spans; 0 means no retention
observability.processing_cleanup_interval_secondsHORIZON_OBSERVABILITY_PROCESSING_CLEANUP_INTERVAL_SECONDS60Processing span TTL cleanup throttle interval in seconds

Capability switches have interdependencies:

  • waits requires event_metrics=true and queued_waits_max>0
  • high_value_detail requires at least one detail channel (failed_detail_enabled, poison_detail_enabled, slow_job_detail_enabled) to be enabled, and a sample rate > 0
  • processing_spans requires event_metrics=true and processing_spans_max>0

Collection & Writing ​

Config KeyEnvironment VariableDefault (full)Description
observability.metrics_windowHORIZON_OBSERVABILITY_METRICS_WINDOW1mEvent time aggregation bucket width for event_metrics; independent of flush_interval
observability.flush_intervalHORIZON_OBSERVABILITY_FLUSH_INTERVAL1mPeriodic flush interval from collector to Store
observability.flush_timeoutHORIZON_OBSERVABILITY_FLUSH_TIMEOUT5sWait limit for a single Store write during flush
observability.batch_sizeHORIZON_OBSERVABILITY_BATCH_SIZE500Maximum number of increments or details written in a single flush
observability.batch_summary_sizeHORIZON_OBSERVABILITY_BATCH_SUMMARY_SIZE500Maximum number of batch summaries retained or written per cycle; defaults to batch_size when not explicitly configured
observability.buffer_sizeHORIZON_OBSERVABILITY_BUFFER_SIZE10000Number of entries in the bounded memory buffer between collector and flusher
observability.max_events_per_secondHORIZON_OBSERVABILITY_MAX_EVENTS_PER_SECOND0Collector ingress rate limit; 0 means no additional rate limiting
observability.drop_policyHORIZON_OBSERVABILITY_DROP_POLICYdrop_oldestDrop policy when the buffer is full or degraded: drop_oldest (prefer dropping oldest) or drop_newest (drop current entry)

Data flow: queue event → Collector (non-blocking enqueue) → in-memory aggregation windows → Flusher (periodic batch write) → Store

Sampling ​

Config KeyEnvironment VariableDefault (full)Description
observability.event_metrics_sample_rateHORIZON_OBSERVABILITY_EVENT_METRICS_SAMPLE_RATE1Sampling rate for queue events entering the event_metrics pipeline. 1 means full, 0 means disabled, 0.1 means 10%
observability.high_value_detail_sample_rateHORIZON_OBSERVABILITY_HIGH_VALUE_DETAIL_SAMPLE_RATEemptyIndependent sampling rate for failed, poison, and slow job details; empty inherits the current actual event_metrics sampling rate
observability.sample_reservoir_sizeHORIZON_OBSERVABILITY_SAMPLE_RESERVOIR_SIZE2048Sample reservoir size for P95/P99 estimation
observability.max_aggregate_keysHORIZON_OBSERVABILITY_MAX_AGGREGATE_KEYS10000Aggregate key cardinality limit (connection+queue/job type); prevents memory exhaustion from too many keys
observability.aggregate_key_ttlHORIZON_OBSERVABILITY_AGGREGATE_KEY_TTL30mExpiration window for low-activity aggregate keys
observability.dynamic_sampling_enabledHORIZON_OBSERVABILITY_DYNAMIC_SAMPLING_ENABLEDtrueWhether to allow dynamic reduction of the actual sampling rate under pressure
observability.min_sample_rateHORIZON_OBSERVABILITY_MIN_SAMPLE_RATE0.01Minimum sampling rate during dynamic sampling degradation

Sampling rate affects data quality labels:

Quality LabelMeaning
exactDerived from full events; no sampling or drop degradation
estimatedDerived from sampled event estimation
degradedAffected by drops, Store failures, or other degradation
unknownUnquantifiable loss exists; cannot be displayed as estimated or exact
partialShutdown best-effort flush wrote an incomplete window

High-Value Diagnostic Details ​

Config KeyEnvironment VariableDefault (full)Description
observability.failed_detail_enabledHORIZON_OBSERVABILITY_FAILED_DETAIL_ENABLEDtrueWhether to save failed job safe summaries
observability.poison_detail_enabledHORIZON_OBSERVABILITY_POISON_DETAIL_ENABLEDtrueWhether to save poison envelope summaries for unparseable payloads
observability.slow_job_detail_enabledHORIZON_OBSERVABILITY_SLOW_JOB_DETAIL_ENABLEDtrueWhether to save slow job summaries
observability.slow_job_thresholdHORIZON_OBSERVABILITY_SLOW_JOB_THRESHOLD30sSlow job diagnostic threshold

High-value diagnostic details only save safe summaries (connection, queue, job name, runtime, truncated error summary); they do not save job payloads, raw envelopes, or complete error stacks.

Data Retention & Cleanup ​

Config KeyEnvironment VariableDefault (full)Description
observability.event_metrics_retentionHORIZON_OBSERVABILITY_EVENT_METRICS_RETENTION24hRetention period for event_metrics aggregated data
observability.high_value_detail_retentionHORIZON_OBSERVABILITY_HIGH_VALUE_DETAIL_RETENTION24hRetention period for high-value diagnostic details
observability.batch_summary_retentionHORIZON_OBSERVABILITY_BATCH_SUMMARY_RETENTION24hRetention period for batch summaries
observability.diagnostics_retentionHORIZON_OBSERVABILITY_DIAGNOSTICS_RETENTION24hRetention period for drop/degradation diagnostic data

Expired data is cleaned up by the Store's Trim operation, which is executed periodically in the supervisor runtime loop.

Diagnostics & Degradation ​

When the collector/flusher experiences drops or degradation, ObservabilityDiagnostic records are written with stable reason identifiers and counts. Common drop reasons:

ReasonDescription
buffer_fullBounded buffer is full, causing observability data to be dropped
rate_limitedmax_events_per_second rate limiting
aggregate_key_overflowAggregate key cardinality limit reached
store_unavailableFlush Store write failure or timeout
flush_lag_exceededPeriodic flush lag exceeds safe window since last success
flush_timeout_nearFlush duration approaches or exceeds flush_timeout
batch_summary_limitBatch summary memory or write limit reached
collector_panicCollector main loop panicked and auto-restarted
flusher_panicFlusher main loop panicked and auto-restarted

Diagnostic data can be viewed through the /metrics/current API diagnostics field and the horizon:snapshot command.

Metrics ​

Horizon monitors queue events through the collector and aggregates them into event_metrics windows. Each window is attributed by event time and contains the following counts:

MetricDescription
processedNumber of successfully processed jobs
failedNumber of failed jobs
releasedNumber of released/retried jobs
poisonNumber of unparseable poison envelopes
queuedNumber of queued jobs
runtime_msTotal job runtime in milliseconds

Windows also include sampling rates and quality labels for Dashboard display of data trustworthiness.

Manually generate a snapshot:

bash
go run ./ horizon:snapshot

horizon:snapshot waits for events already accepted by the collector before the command invocation to finish aggregation before persisting the snapshot. No additional delay is needed to wait for the background collector.

Clear metrics:

bash
go run ./ horizon:clear-metrics

Failed Jobs ​

Horizon does not replace github.com/prismgo/framework/queue's FailedStore, but provides the horizon:forget command for maintaining failed records:

bash
# Delete a single failed record
go run ./ horizon:forget <id>

# Delete all failed records
go run ./ horizon:forget --all

Safe summaries of failed jobs (without payloads or complete stacks) are collected through the high-value diagnostic detail channel and can be viewed on the Dashboard at /high-value-detail?kind=failed.

Long Wait Events ​

When queue wait times exceed the thresholds configured in horizon.waits, Horizon emits a horizon.long_wait event. Applications can listen for this event through github.com/prismgo/framework/event to convert it into notifications, logs, or external alerts.

go
event.Listen("horizon.long_wait", func(ctx context.Context, e event.Event) error {
    var evt horizon.LongWaitEvent
    // Parse the event, which contains connection, queue, threshold_ms, wait_ms, sampled_at
    return nil
})

LongWaitEvent only contains connection, queue, threshold, observed value, and sampling time; it does not contain job payloads.

Batch Summaries ​

When observability.batch_summaries is enabled, Horizon listens for queue.EventBatchCreated, queue.EventBatchUpdated, queue.EventBatchCancelled, and queue.EventBatchFinished events to generate batch safe summaries.

Batch summaries only save progress, status, and timestamps; they do not expose internal batch job payloads, raw envelopes, or broker fields. They can be viewed through the Dashboard's /batches API.

Store Interface ​

The Horizon Store is an abstract state storage interface that supports Redis and Memory implementations. The Store holds the following data:

Data TypeDescription
Master/Supervisor/Worker heartbeatProcess heartbeats for liveness detection and status display
ControlStateGlobal and supervisor-level control flags (pause/terminate)
StatusSnapshotGlobal runtime status derived view
EventMetricWindowEvent time aggregation windows
QueueLengthSnapshotQueue length samples
HighValueJobDetailHigh-value diagnostic summaries
ObservabilityDiagnosticDrop and degradation diagnostics
BatchSummaryBatch safe summaries
OrphanProcessOrphan process records

The Store interface defines 30+ methods covering heartbeat writes, control flags, metrics reads/writes, diagnostics, and orphan process management. Redis Store is recommended for production; Memory Store can be used for testing.

Note: Memory Store is only suitable for local/testing environments and is not recommended for production. When using Memory Store, Horizon outputs a warning at command startup.

Deploying Horizon ​

Process Management ​

The Horizon master is managed by systemd, supervisord, Docker, or Kubernetes. Deployment workflow:

  1. Send a horizon:terminate request to gracefully exit the current master
  2. The process manager detects the exit and starts a new master
  3. The new master loads the latest configuration and spawns supervisors

Configuration Guidelines ​

  • timeout should be less than the queue connection's retry_after to avoid concurrent duplicate consumption of the same task
  • Authentication, payment, and notification queues should be split into separate supervisors for independent scaling
  • Keep HORIZON_PREFIX distinct from business queue key prefixes to avoid state confusion
  • For high-throughput production environments, prefer production_light or reduce sampling rates
  • When job type, tenant, or tag cardinality is high, prioritize limiting max_aggregate_keys, reducing sampling rates, or using production_light

Removed Configuration ​

The following configuration fields and environment variables have been removed and will cause errors when used:

RemovedReplacement
horizon.trimUse observability.event_metrics_retention / high_value_detail_retention / batch_summary_retention / diagnostics_retention
horizon.silenced / horizon.silenced_tagsHigh-value details are no longer filtered by silenced rules
horizon.metrics.trim_snapshotsUse observability.event_metrics_retention
observability.recent_jobsUse failed_detail_enabled or poison_detail_enabled
observability.recent_jobs_maxUse buffer_size or max_aggregate_keys
observability.job_history / observability.queue_historyUse event_metrics
observability.success_detail_enabledSuccessful job details have been removed and are no longer collected
HORIZON_TRIM_RECENT / HORIZON_TRIM_FAILED / HORIZON_TRIM_MONITOREDUse the corresponding retention environment variables
HORIZON_SILENCED / HORIZON_SILENCED_TAGSSilenced rules have been removed