Skip to content

Queue-Metrics Integration

Queue-Metrics Integration

Queue Monitor is built on top of laravel-queue-metrics and subscribes to its event system to capture per-job resource data.

How Integration Works

The integration happens through event subscription:

laravel-queue-metrics fires JobMetricsCompleted / JobMetricsFailed
              ↓
QueueMetricsSubscriber listens
              ↓
JobMonitorRepository updates job record
              ↓
CPU, memory metrics stored

Metrics Captured

CPU Time

Source: ProcessMetrics tracking in queue-metrics Field: cpu_time_ms (decimal) Calculation: User + System CPU time in milliseconds

$job = QueueMonitor::getJob($uuid);
echo "CPU Time: {$job->cpu_time_ms}ms";

Memory Peak

Source: ProcessMetrics tracking in queue-metrics Field: memory_peak_mb (decimal) Includes: Parent + child process memory

echo "Peak Memory: {$job->memory_peak_mb}MB";

File Descriptors

Source: Custom metrics from queue-metrics (if available) Field: file_descriptors (integer) Note: May not be available on all systems

if ($job->file_descriptors !== null) {
    echo "File Descriptors: {$job->file_descriptors}";
}

Event Flow

1. Job Processing

// Laravel Queue fires JobProcessing event
// queue-monitor records job start

// queue-metrics starts ProcessMetrics tracking
ProcessMetrics::start("job_{$jobId}");

2. Job Completion

// Job finishes executing

// queue-metrics fires a per-job metrics event
event(new JobMetricsCompleted($jobId, $cpuTimeMs, $memoryMb, $workerMemoryLimitMb));

// queue-monitor's subscriber handles it
QueueMetricsSubscriber::handleJobMetricsCompleted($event);

3. Data Storage

QueueMetricsSubscriber subscribes to JobMetricsCompleted and JobMetricsFailed (from cboxdk/laravel-queue-metrics) and updates the job record with the metrics carried on the event:

$this->repository->update($jobMonitor->uuid, [
    'cpu_time_ms' => $cpuTimeMs,
    'memory_peak_mb' => $memoryMb,
    'worker_memory_limit_mb' => $workerMemoryLimitMb,
]);

Matching Metrics to an Attempt

A retried or redelivered job has one row per attempt, all sharing one job_id. JobMetricsCompleted and JobMetricsFailed carry that job_id but not the attempt number, so the subscriber picks the row in this order:

  1. The attempt this worker started. When a job starts, JobProcessingListener remembers in the worker process which attempt it began. The metrics event is raised synchronously in the same worker when the job ends, so that attempt is the one that was measured.
  2. The attempt still processing. This is used when the worker did not see the start.
  3. The latest attempt with no metrics yet. This is used when the row has already moved to completed or failed.

The first rule matters when the broker redelivers a slow job to a second worker while the first is still running it. The newer attempt is then the latest row, and the one still processing, but its figures belong to the second worker. See Job Lifecycle and Attempts.

Accessing Metrics

Via Model

use Cbox\LaravelQueueMonitor\Models\JobMonitor;

$job = JobMonitor::find($id);

echo "CPU: {$job->cpu_time_ms}ms\n";
echo "Memory: {$job->memory_peak_mb}MB\n";
echo "Duration: {$job->duration_ms}ms\n";

Via Facade

use Cbox\LaravelQueueMonitor\Facades\LaravelQueueMonitor as QueueMonitor;

$job = QueueMonitor::getJob($uuid);

if ($job->cpu_time_ms !== null) {
    $cpuEfficiency = ($job->cpu_time_ms / $job->duration_ms) * 100;
    echo "CPU Efficiency: {$cpuEfficiency}%\n";
}

Via API

curl /api/queue-monitor/jobs/{uuid}

Response includes metrics:

{
  "data": {
    "uuid": "...",
    "metrics": {
      "cpu_time_ms": 245.50,
      "memory_peak_mb": 48.25,
      "file_descriptors": 15,
      "duration_ms": 1250,
      "duration_seconds": 1.25
    }
  }
}

Analytics with Metrics

Statistics endpoints automatically include metric aggregations:

$stats = QueueMonitor::statistics();

echo "Avg CPU Time: {$stats['avg_cpu_time_ms']}ms\n";
echo "Max Memory: {$stats['max_memory_mb']}MB\n";
echo "Avg Duration: {$stats['avg_duration_ms']}ms\n";

Performance Analysis

Slow Jobs

Find jobs with high resource usage:

use Cbox\LaravelQueueMonitor\Models\JobMonitor;

// High CPU jobs
$cpuIntensive = JobMonitor::where('cpu_time_ms', '>', 1000)
    ->orderByDesc('cpu_time_ms')
    ->get();

// High memory jobs
$memoryIntensive = JobMonitor::where('memory_peak_mb', '>', 100)
    ->orderByDesc('memory_peak_mb')
    ->get();

// Slow jobs
$slowJobs = JobMonitor::slowJobs(5000) // > 5 seconds
    ->get();

Per-Queue Resource Usage

$queueStats = QueueMonitor::serverStatistics();

foreach ($queueStats as $stat) {
    echo "Queue: {$stat['queue']}\n";
    echo "Avg Duration: {$stat['avg_duration_ms']}ms\n";
    echo "Total Jobs: {$stat['total']}\n";
}

Configuration

Persistence

Queue-metrics has two layers: instrumentation (per-job CPU/memory events) and persistence (aggregate storage of workers, throughput, baselines).

Queue Monitor only needs the instrumentation layer. If you don't use queue-autoscale or Prometheus export, you can disable persistence:

// config/queue-metrics.php
'persistence' => [
    'enabled' => env('QUEUE_METRICS_PERSISTENCE', true),
],

With persistence off, per-job CPU/memory events still fire and Queue Monitor captures them. No storage backend required.

Note: cboxdk/laravel-queue-autoscale requires persistence enabled. It reads worker heartbeats, throughput, and baselines to make scaling decisions.

Storage Backend (when persistence is enabled)

'storage' => [
    // 'redis' (default, recommended) or 'database'
    'driver' => env('QUEUE_METRICS_STORAGE', 'redis'),
    'connection' => env('QUEUE_METRICS_CONNECTION', 'default'),
    'prefix' => 'queue_metrics',
    // Recommended: 1000 for Redis, 500 for database
    'max_samples_per_key' => env('QUEUE_METRICS_MAX_SAMPLES', 1000),
],

Redis is the recommended driver. Database is available for low-scale workloads (< 10 workers). See the installation guide for setup.

Metrics Collection

Queue-metrics instruments jobs automatically. No configuration needed beyond persistence and storage settings.

Integration Settings

Queue-monitor automatically subscribes to metrics events:

// No configuration needed - automatic integration

Troubleshooting

Metrics Not Appearing

  1. Verify queue-metrics is installed:

    composer show cboxdk/laravel-queue-metrics
    
  2. Check metrics collection is enabled:

    config('queue-metrics.enabled'); // Should be true
    
  3. Verify events are firing:

    Event::listen(JobMetricsCompleted::class, function ($event) {
        Log::info('Metrics recorded', ['job' => $event->jobId, 'cpu_ms' => $event->cpuTimeMs]);
    });
    

Partial Metrics

Some metrics may be unavailable on certain systems:

  • File descriptors: Requires system support
  • CPU time: Available on most *nix systems
  • Memory: Always available via PHP

Performance Impact

Metrics collection has minimal overhead:

  • CPU tracking: <1% overhead
  • Memory tracking: <0.1% overhead
  • Sample rate can be adjusted to reduce load

Best Practices

  1. Enable in all environments - Metrics are valuable in dev, staging, and production
  2. Use sample rates in high-traffic - Reduce overhead with sampling
  3. Monitor the monitors - Track queue-monitor's own performance
  4. Correlate with application metrics - Combine with APM tools
  5. Set up alerts - React to resource anomalies automatically