Download the PHP package cboxdk/laravel-queue-autoscale without Composer
On this page you can find all versions of the php package cboxdk/laravel-queue-autoscale. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download cboxdk/laravel-queue-autoscale
More information about cboxdk/laravel-queue-autoscale
Files in cboxdk/laravel-queue-autoscale
Package laravel-queue-autoscale
Short Description Intelligent, predictive autoscaling for Laravel queues with SLA/SLO-based optimization
License MIT
Homepage https://github.com/cboxdk/laravel-queue-autoscale
Informations about the package laravel-queue-autoscale
Cbox Queue Autoscale
SLA-driven autoscaling for Laravel queue workers.
Queue Autoscale for Laravel is a long-running worker manager that spawns and terminates queue:work
processes to hold a pickup-time SLA. Instead of configuring worker counts, you declare how long a job
may wait before it is picked up, and the manager solves for the worker count each evaluation cycle
using queueing theory (Little's Law) and backlog-drain math, bounded by measured CPU and memory
capacity on the host.
Features
- SLA-based scaling — declare a target pickup time; worker counts are derived, not configured
- Little's Law steady state —
workers = arrival rate × average job timefor the baseline - Backlog drain with progressive urgency — a quadratic aggressiveness curve that ramps from 1.0x at half the SLA budget to 3.0x at the SLA target, capped at 5.0x
- p95 pickup-time signal — sliding-window percentile over real observed pickup times, with a fallback to oldest-job age when there are not enough samples
- Spawn-latency compensation — measured spawn time (EMA) is subtracted from the SLA budget
- Failure fuse — a downstream outage looks like load to any autoscaler; the fuse detects a high
failure rate, holds the queue at
workers.min, then probes with a single worker before releasing - Resource-aware — CPU and memory ceilings measured on the host constrain every decision
- Metrics-driven — queue discovery and metrics come from
laravel-queue-metrics - Cluster-aware — managers auto-join via Redis, elect a leader, and distribute worker targets across hosts
- Worker groups — one worker set polling several queues in strict priority order
- Queues matched by pattern —
scrape-tenant-*governs every tenant queue, so runtime-generated names need no configuration entry of their own - Configuration check —
queue:autoscale:doctorreports configurations that are valid and still govern the wrong queues - Testable — fakes and assertions in
src/Testingfor proving what your own configuration does - Extensible — custom scaling strategies and policies via interfaces
- Events — react to scaling decisions, SLA breaches, fuse transitions and cluster changes
- Graceful shutdown — SIGTERM, then SIGKILL after the shutdown timeout
Requirements
- PHP 8.4 or 8.5
- Laravel 12 or 13
ext-pcntlandext-posix(the manager is a signal-handling daemon)cboxdk/laravel-queue-metrics^3.0
Redis is required only for cluster mode. Single-host mode works with any queue driver and needs no
Redis. cboxdk/laravel-telemetry is optional and
enables the OpenTelemetry integration; it requires Laravel 12+.
Installation
Run the interactive installer to publish config, choose a topology, and generate matching .env
values:
It offers three presets via --topology=:
| Preset | Shape |
|---|---|
single-low |
single host, low traffic, no Redis infrastructure |
single-redis |
single host with Redis-backed metrics and predictive signals |
cluster |
multi-host cluster with Redis coordination |
Additional flags: --metrics-connection=, --publish-migrations, --write-env, --env-file=,
--force, --no-publish.
If you prefer the manual path, publish the config yourself:
Set up the metrics package
This package does not discover queues or collect metrics itself. Both come from
laravel-queue-metrics, which is installed as a
dependency:
Configure its storage backend in .env:
If you use database storage, publish and run its migrations:
Choose an autoscale topology
auto keeps single-host mode Redis-free and switches to Redis-backed coordination in cluster mode.
The installer can write these values into .env for you with --write-env.
Quick Start
1. Configure SLA targets (optional)
By default sla_defaults is BalancedProfile::class — a 30-second p95 pickup-time target with
1–10 workers. If that suits you, there is nothing to configure.
To customise, edit config/queue-autoscale.php. Each entry is either a ProfileContract class
or a literal array that is deep-merged over sla_defaults:
A per-queue entry does not accept 'profile' and 'overrides' keys — those belong to groups
only. See Workload Profiles.
2. Run the autoscaler
Each cycle the manager:
- Pulls queue metrics from
laravel-queue-metrics - Runs the configured strategy to get a target worker count
- Constrains that target by measured host CPU/memory capacity
- Clamps it to
workers.min/workers.max - Applies the failure fuse
- Runs the registered policies over the resulting decision
- Spawns or terminates workers, and dispatches events
3. Monitor with events
How It Works
The hybrid strategy
HybridStrategy (the default) computes two candidate worker counts and takes the maximum:
1. Steady state — Little's Law
The arrival rate comes from ArrivalRateEstimator, which tracks backlog deltas and blends in a
forecast. It is used only when its confidence clears scaling.min_arrival_rate_confidence (0.5);
otherwise the observed processing rate (throughputPerMinute / 60) is used instead. When the
failure rate exceeds 5%, an estimated retry volume is subtracted so that retries are not counted as
new arrivals.
2. Backlog drain — SLA protection
This returns zero until slaProgress reaches scaling.breach_threshold (default 0.5, i.e. half
the SLA budget consumed). The multiplier then ramps continuously: 1.0x at 50%, ~1.72x at 80%,
3.0x at 100%, capped at 5.0x. effectiveSla is the SLA target minus the measured spawn latency, and
slaSignal is the p95 of recent observed pickup times, falling back to oldest-job age when there
are too few samples.
The larger of the two wins. A saturation guard then bumps the target to activeWorkers + 1 if
workers are above 90% utilisation but neither calculation asked for more. Finally the target is
clamped to [workers.min, workers.max] and passed through a hysteresis smoother that limits
scale-down to one worker per cycle while throughput is stable.
Constraints applied to every decision
- Host capacity — CPU and memory ceilings from measured system metrics, expressed as
currentWorkers + additional headroomand reduced by what other queues on the host already use - Config bounds —
workers.minandworkers.maxfrom the queue's profile - Failure fuse — holds a queue at
workers.minwhile its failure rate is above threshold - Anti-flapping cooldown —
scaling.cooldown_seconds(default 60) blocks only a scale-down, and only while the window opened by a recent scale-up is still running; scaling further in the same direction is always allowed, and a scale-up is never held
See the Architecture deep dive for the full derivation.
Configuration Reference
The published config/queue-autoscale.php is documented inline. The keys most people touch:
A per-queue entry may also carry a resources block declaring cold-start CPU/memory estimates for
that queue's workers:
manager.evaluation_interval_seconds (default 5) sets the evaluation interval;
queue:autoscale --interval= overrides it for a single process.
Every key, including the profile, forecast, fuse, pickup-time, spawn-latency, cluster, alerting and telemetry blocks, is covered in Configuration.
Custom Scaling Strategies
A strategy answers one question: how many workers should this queue have right now?
Register it as a plain class string:
The engine still applies capacity limits, config bounds and the fuse on top of whatever a strategy returns. See Custom Strategies.
Scaling Policies
A policy runs after the strategy and engine have produced a ScalingDecision. beforeScaling()
may return a modified decision (or null to leave it alone); afterScaling() observes the result.
Register class strings — the loader resolves each through the container, so constructor injection works. An instance or closure placed in this array is silently ignored:
Policies are chained: a non-null return becomes the decision the next policy sees. An exception thrown by a policy is caught and logged, and scaling continues. See Scaling Policies.
Events
All events live in Cbox\LaravelQueueAutoscale\Events.
| Event | Properties |
|---|---|
ScalingDecisionMade |
decision |
SlaBreachPredicted |
decision |
WorkersScaled |
connection, queue, from, to, action, reason |
SlaBreached |
connection, queue, oldestJobAge, slaTarget, pending, activeWorkers |
SlaRecovered |
connection, queue, currentJobAge, slaTarget, pending, activeWorkers |
FuseTripped |
connection, queue, failureRate, samples, failures, thresholdPercent, heldAtWorkers |
FuseProbing |
connection, queue, probeWorkers, cooldownSeconds |
FuseRecovered |
connection, queue, failureRate, samples |
AutoscaleManagerStarted |
managerId, host, clusterEnabled, clusterId, intervalSeconds, startedAt, packageVersion |
AutoscaleManagerStopped |
managerId, host, clusterEnabled, clusterId, startedAt, stoppedAt, reason, workerCount, packageVersion |
ClusterLeaderChanged |
clusterId, previousLeaderId, currentLeaderId, observedByManagerId, changedAt |
ClusterManagerPresenceChanged |
clusterId, managerIds, addedManagerIds, removedManagerIds, leaderId, observedByManagerId, observedAt |
ClusterSummaryPublished |
clusterId, leaderId, summary, publishedAt |
WorkersScaled::$action is the literal string 'up' or 'down'. ScalingDecision::action() uses a
different vocabulary ('scale_up', 'scale_down', 'hold') — do not mix them up. There is no
confidence property on ScalingDecision, and no worker-health event.
See Event Handling.
Running as a Daemon
The manager is a long-running process. Run exactly one per app in single-host mode, and exactly one per host in cluster mode. Use Supervisor to keep it alive:
On deploy, restart it through Artisan so it drains its workers before Supervisor starts the new release:
With manager.honor_queue_restart enabled (the default), a plain php artisan queue:restart also
stops the manager gracefully, so a standard deploy pipeline needs no extra step.
See Deployment for Forge, Ploi, Docker and self-hosted recipes.
Metrics Integration
This package does not discover queues or collect metrics itself. Both come from
laravel-queue-metrics:
QueueMetrics::getAllQueuesWithMetrics() returns a keyed array of raw metric arrays; use
getQueueMetrics() when you want the QueueMetricsData object.
Division of responsibility
| laravel-queue-metrics | laravel-queue-autoscale |
|---|---|
| Scans configured queue connections | Applies the scaling algorithms |
| Discovers active queues | Makes SLA-based scaling decisions |
| Collects depth, age and duration metrics | Manages the worker pool lifecycle |
| Calculates throughput and failure rates | Enforces CPU/memory constraints |
| Tracks worker heartbeats | Runs policies and dispatches events |
OpenTelemetry via laravel-telemetry
When cboxdk/laravel-telemetry is installed, the
autoscaler publishes its scaling signals automatically — no configuration needed. Disable with
QUEUE_AUTOSCALE_TELEMETRY_ENABLED=false.
When it is not installed, queue:autoscale:debug reports Telemetry: not installed and everything
else carries on unchanged — the integration is optional, not a dependency.
| Metric | Type | Unit | Labels |
|---|---|---|---|
queue_autoscale.workers.target |
gauge | {workers} |
connection, queue |
queue_autoscale.sla.predicted_pickup |
gauge | s |
connection, queue |
queue_autoscale.sla.target |
gauge | s |
connection, queue |
queue_autoscale.sla.breach |
gauge | 1 |
connection, queue |
queue_autoscale.capacity.max_workers |
gauge | {workers} |
limiter |
queue_autoscale.fuse.state |
gauge | 1 |
connection, queue |
queue_autoscale.scaling.actions |
counter | {actions} |
connection, queue, direction |
queue_autoscale.sla.breaches |
counter | {breaches} |
connection, queue |
queue_autoscale.fuse.trips |
counter | {trips} |
connection, queue |
queue_autoscale.cluster.leader_changes |
counter | {changes} |
— |
queue_autoscale.cluster.managers |
gauge (observable) | {managers} |
— |
queue_autoscale.cluster.workers |
gauge (observable) | {workers} |
— |
queue_autoscale.cluster.required_workers |
gauge (observable) | {workers} |
— |
queue_autoscale.cluster.worker_capacity |
gauge (observable) | {workers} |
— |
queue_autoscale.cluster.utilization |
gauge (observable) | % |
— |
queue_autoscale.cluster.recommended_hosts |
gauge (observable) | {hosts} |
— |
queue_autoscale.cluster.host_workers |
gauge (observable) | {workers} |
host |
queue_autoscale.cluster.host_capacity |
gauge (observable) | {workers} |
host |
Scaling actions, SLA breaches and recoveries, fuse transitions, manager start/stop and cluster leader
changes are also emitted as structured OTLP events (queue_autoscale.scaling.action,
queue_autoscale.sla.breached, queue_autoscale.fuse.tripped, …) carrying the full context —
including the scaling reason, which is deliberately not a metric label.
Deliberately not exported: queue depth, oldest-job age, health scores, worker busy/idle state and
job baselines (owned by cboxdk/laravel-queue-metrics), and per-job durations/outcomes (covered by
laravel-telemetry's own queue instrumentation). There is no active-worker gauge here —
queue-metrics' queue_metrics.queue.active_workers gauge is the one to join against
queue_autoscale.workers.target in your dashboards.
Metrics are shipped to your OTLP endpoint by the telemetry package's telemetry:flush (cron or
--daemon) — make sure one is scheduled.
Testing
Testing your own configuration
The package ships fakes and assertions so an application can prove what its queues will do, without Redis and without waiting for load:
See Testing Your Configuration.
Testing the package itself
SQS and FIFO specs run against ElasticMQ and skip when it is not running:
Changelog
Please see CHANGELOG for recent changes.
Contributing
Please see the Contributing Guide for details.
Security
Please report security issues privately through GitHub Private Vulnerability Reporting rather than the public issue tracker. This is a community-maintained package; reports are handled on a best-effort basis, and fixes land on the current major line. See Security.
Credits
- Sylvester Damgaard
- All Contributors
Resources
Documentation
- Introduction — what the package is and when to reach for it
- Quick Start — one queue autoscaled in five minutes
- Installation — full install and configuration walkthrough
- Architecture — deep dive into the algorithms and system design
- Troubleshooting — common issues and debugging
- examples/README.md — templates for custom strategies and policies
Examples
- Custom strategies
- TimeBasedStrategy — scale on time-of-day patterns
- CostOptimizedStrategy — conservative scaling
- Custom policies
- SlackNotificationPolicy — Slack alerts on scaling events
- MetricsLoggingPolicy — log detailed metrics to a dedicated file
examples/config-examples.phpis written against the current schema. The strategy and policy classes implement the real contracts and are meant to be adapted, not dropped in as-is. The authoritative reference is the documentation.
License
The MIT License (MIT). Please see License File for more information.
All versions of laravel-queue-autoscale with dependencies
ext-pcntl Version *
ext-mbstring Version *
ext-posix Version *
cboxdk/laravel-queue-metrics Version ^3.3
illuminate/contracts Version ^12.0||^13.0
symfony/process Version ^7.0||^8.0