Skip to content
Pro Analyzer — Available with ShieldCI Pro

Redis Rate Limiting Analyzer

Analyzer IDCategorySeverityTime To Fix
redis-rate-limiting⚡ PerformanceLow10 minutes

What This Checks

When your application uses Redis, this analyzer suggests using RateLimitedWithRedis instead of RateLimited middleware for queue jobs to ensure atomic rate limiting across multiple workers.

Why It Matters

  • Multi-Worker Accuracy: Standard rate limiting can have race conditions with multiple queue workers
  • Atomic Operations: Redis-based limiting uses atomic Lua scripts for accurate counting
  • Distributed Queues: Essential when running multiple queue workers or Horizon
  • Consistent Behavior: Ensures rate limits are enforced exactly as configured

When multiple queue workers process jobs simultaneously, non-Redis rate limiting can allow bursts beyond your configured limits.

How to Fix

Quick Fix (5 minutes)

Replace RateLimited with RateLimitedWithRedis in each job's middleware() method:

Before (❌):

php
use Illuminate\Queue\Middleware\RateLimited;

public function middleware(): array
{
    return [new RateLimited('podcasts')];
}

After (✅):

php
use Illuminate\Queue\Middleware\RateLimitedWithRedis;

public function middleware(): array
{
    return [new RateLimitedWithRedis('podcasts')];
}

Proper Fix (10 minutes)

Define named rate limiters centrally in AppServiceProvider, then apply RateLimitedWithRedis to all affected jobs:

app/Providers/AppServiceProvider.php:

php
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;

public function boot(): void
{
    RateLimiter::for('podcasts', function (object $job) {
        return Limit::perMinute(10);
    });

    RateLimiter::for('email', function (object $job) {
        return Limit::perMinute(3)->by($job->user->id);
    });
}

Each affected job:

php
<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\Middleware\RateLimitedWithRedis;

class ProcessPodcast implements ShouldQueue
{
    use Queueable;

    public function middleware(): array
    {
        return [new RateLimitedWithRedis('podcasts')];
    }

    public function handle(): void { /* ... */ }
}

If you also use WithoutOverlapping, ensure Redis is your cache driver for reliable distributed locks:

ini
# Laravel 11+
CACHE_STORE=redis
# Laravel 10 and below
CACHE_DRIVER=redis

References