Queue Routing and Failover (Laravel 13)

+15 Mana ✨

Introduction

Laravel 13 added two features that change how production apps move jobs between brokers: Queue::route() centralizes the "which connection does this job use?" decision, and the failover driver lets a second broker take over when the primary is down. Before 13, each job had to call onConnection() / onQueue() itself — if you wanted to move all payment jobs to a new Redis cluster, you had to grep the codebase.

Key Concepts

  • Queue routing: A rulebook registered once in a service provider that says this job class lives on this connection and queue.
  • Failover driver: A meta-driver that wraps two or more real drivers and pushes jobs to the first healthy one.
  • Deferred/background connections: Special connection names that run jobs after the HTTP response is flushed, without a worker.

Real World Context

A team moving from a single Redis to a Redis + SQS setup used to sprinkle ->onConnection('sqs') across dozens of controllers. With Queue::route(), the mapping is one file. And when Redis goes down at 3am, the failover driver routes to SQS instead of filling up failed_jobs.

Deep Dive

Centralized routing with Queue::route

Register routes in a service provider so you never repeat onConnection at the call site:

php
use Illuminate\Support\Facades\Queue;
use App\Jobs\ProcessPayment;
use App\Jobs\SendReceipt;

public function boot(): void
{
    Queue::route(ProcessPayment::class, connection: 'redis', queue: 'payments');
    Queue::route(SendReceipt::class,   connection: 'sqs',   queue: 'emails');
}

Now anywhere in the app you can just write ProcessPayment::dispatch($order) and it lands on the right broker.

The failover driver

Configure a failover connection in config/queue.php:

php
'connections' => [
    'redis' => [
        'driver' => 'redis',
        // ...
    ],
    'sqs' => [
        'driver' => 'sqs',
        // ...
    ],
    'failover' => [
        'driver' => 'failover',
        'connections' => ['redis', 'sqs'],
    ],
],

Dispatch through the failover connection:

php
ProcessReport::dispatch($data)->onConnection('failover');

If pushing to redis throws, Laravel transparently tries sqs — the caller never sees the error.

Deferred and background connections

Laravel 13 formalized two special connection names:

php
// Runs after the response is sent — no worker needed
SendWelcomeEmail::dispatch($user)->onConnection('deferred');

// Runs in a background PHP process — also no worker needed
GenerateThumbnail::dispatch($upload)->onConnection('background');

These replace the older dispatchAfterResponse() helper and give you a clean way to fire-and-forget small jobs without running Supervisor.

Common Pitfalls

  1. Registering routes inside a controller — Queue::route() must live in a service provider's boot() method, or the mapping won't exist when jobs are dispatched from other parts of the app.
  2. Putting SQS first in a failover list — the first driver is the preferred one. If you list SQS first because it's "more reliable", every job pays the network round-trip even when Redis is healthy.

Best Practices

  1. Route by job class, not by call site — one service-provider file is easier to audit than grepping for onConnection.
  2. Keep failover lists short (2–3 drivers) — long failover chains mask real outages and make debugging harder.

Summary

  • Queue::route() maps job classes to connections/queues in one place.
  • The failover driver tries brokers in order until one succeeds.
  • deferred and background connections run small jobs without a worker.
✓ Completed