Skip to content

Alert on a Fuse Trip

Alert on a Fuse Trip

A tripped failure fuse is a stronger signal than a slow queue: it means the queue's jobs are failing, not merely waiting. This recipe wires the three fuse events to alerts.

Fuse events fire on state transitions only, never per evaluation cycle, so a sustained outage produces one alert rather than one every five seconds. You do not need to rate-limit these listeners the way you do for SLA breach events.

1. Write the listener

app/Listeners/AlertOnFuseTrip.php:

<?php

namespace App\Listeners;

use Cbox\LaravelQueueAutoscale\Events\FuseProbing;
use Cbox\LaravelQueueAutoscale\Events\FuseRecovered;
use Cbox\LaravelQueueAutoscale\Events\FuseTripped;
use Illuminate\Support\Facades\Log;

class AlertOnFuseTrip
{
    public function handleTripped(FuseTripped $event): void
    {
        Log::channel('queue-alerts')->critical('Queue autoscaling held back by failure fuse', [
            'queue' => $event->queue,
            'connection' => $event->connection,
            'failure_rate' => round($event->failureRate, 1),
            'threshold' => $event->thresholdPercent,
            'samples' => $event->samples,
            'held_at_workers' => $event->heldAtWorkers,
        ]);
    }

    public function handleProbing(FuseProbing $event): void
    {
        Log::channel('queue-alerts')->info('Probing for recovery', [
            'queue' => $event->queue,
            'probe_workers' => $event->probeWorkers,
        ]);
    }

    public function handleRecovered(FuseRecovered $event): void
    {
        Log::channel('queue-alerts')->notice('Failure fuse closed, scaling resumed', [
            'queue' => $event->queue,
            'failure_rate' => round($event->failureRate, 1),
        ]);
    }
}

See Alert via Laravel Log for setting up the queue-alerts channel, or swap the Log calls for the Slack or email approaches in Alert via Slack and Alert via Email.

2. Register it

app/Providers/EventServiceProvider.php:

use App\Listeners\AlertOnFuseTrip;
use Cbox\LaravelQueueAutoscale\Events\FuseProbing;
use Cbox\LaravelQueueAutoscale\Events\FuseRecovered;
use Cbox\LaravelQueueAutoscale\Events\FuseTripped;

protected $listen = [
    FuseTripped::class => [[AlertOnFuseTrip::class, 'handleTripped']],
    FuseProbing::class => [[AlertOnFuseTrip::class, 'handleProbing']],
    FuseRecovered::class => [[AlertOnFuseTrip::class, 'handleRecovered']],
];

3. Write a message worth waking up to

The default payload tells you which queue is failing and how badly. What it cannot tell you is what is failing — that lives in your job exceptions. A useful alert points at both:

public function handleTripped(FuseTripped $event): void
{
    $recentFailure = \DB::table('failed_jobs')
        ->where('queue', $event->queue)
        ->latest('failed_at')
        ->value('exception');

    Log::channel('queue-alerts')->critical(sprintf(
        '%s: %.0f%% of jobs failing (%d samples). Autoscaling held at %d workers. Latest: %s',
        $event->queue,
        $event->failureRate,
        $event->samples,
        $event->heldAtWorkers,
        str($recentFailure ?? 'unknown')->limit(200),
    ));
}

Note that failed_jobs only fills up once jobs exhaust their retries, so on a fresh outage it may lag the fuse by a few minutes. The fuse counts thrown exceptions immediately — that gap is intentional, and explained in What counts as a failure.

Measuring incident duration

Pair the trip and recovery events to record how long each incident lasted:

use Illuminate\Support\Facades\Cache;

public function handleTripped(FuseTripped $event): void
{
    Cache::put("fuse-trip:{$event->connection}:{$event->queue}", now()->timestamp, 86400);
    // ...alert
}

public function handleRecovered(FuseRecovered $event): void
{
    $key = "fuse-trip:{$event->connection}:{$event->queue}";
    $startedAt = Cache::pull($key);

    if ($startedAt !== null) {
        Log::channel('queue-alerts')->notice('Incident resolved', [
            'queue' => $event->queue,
            'duration_seconds' => now()->timestamp - $startedAt,
        ]);
    }
}

If you run cboxdk/laravel-telemetry, you get this for free — queue_autoscale.fuse.state is a gauge you can measure directly, no bookkeeping required. See Telemetry.