Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Rate Limited Job Middleware Laravel Package

spatie/laravel-rate-limited-job-middleware

Laravel job middleware to rate limit queued jobs. Allow a configurable number of jobs per second and automatically release throttled jobs for a delay. Easy to attach per job via middleware, with support for time-based attempts when rate limiting.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require spatie/laravel-rate-limited-job-middleware
    

    Publish the config file (optional):

    php artisan vendor:publish --provider="Spatie\RateLimitedJobMiddleware\RateLimitedJobMiddlewareServiceProvider"
    
  2. Register Middleware: Apply the middleware to a job class in its handle() method or globally via app/Console/Kernel.php:

    protected $middleware = [
        \Spatie\RateLimitedJobMiddleware\RateLimitedJobMiddleware::class,
    ];
    
  3. First Use Case: Configure rate limits in config/rate-limited-job-middleware.php:

    'limits' => [
        'send-notification' => [
            'maxJobs' => 5,
            'perMinute' => true,
            'key' => 'user_id',
        ],
    ],
    

    Then dispatch a job:

    SendNotification::dispatch($user)->onQueue('emails');
    

Implementation Patterns

Core Workflows

  1. Per-Job Rate Limiting: Define limits in config/rate-limited-job-middleware.php with:

    • maxJobs: Maximum allowed jobs.
    • perMinute/perHour/perDay: Time window.
    • key: Dynamic key (e.g., user_id, tenant_id) to scope limits.

    Example:

    'limits' => [
        'process-invoice' => [
            'maxJobs' => 3,
            'perHour' => true,
            'key' => 'invoice_id',
        ],
    ],
    
  2. Global vs. Per-Job Middleware:

    • Global: Apply to all jobs via Kernel.php (use cautiously).
    • Per-Job: Attach only to specific jobs (recommended for granularity):
      class ProcessInvoice implements ShouldQueue {
          use Dispatchable, InteractsWithQueue;
      
          public function handle() {
              // Job logic
          }
      
          public function middleware() {
              return [new \Spatie\RateLimitedJobMiddleware\RateLimitedJobMiddleware()];
          }
      }
      
  3. Dynamic Keys: Use closures or job payloads to dynamically generate keys:

    'limits' => [
        'sync-data' => [
            'maxJobs' => 10,
            'perMinute' => true,
            'key' => function ($job) {
                return "user_{$job->userId}_source_{$job->source}";
            },
        ],
    ],
    
  4. Queue Integration:

    • Works seamlessly with Laravel’s queue drivers (database, redis, sync).
    • For distributed queues, ensure the rate-limiting storage (default: cache) is shared (e.g., Redis).
  5. Fallback Behavior:

    • Default: Jobs exceeding limits are skipped silently.
    • Custom Handling: Override RateLimitedJobMiddleware to log, retry, or throw exceptions:
      class CustomRateLimitedMiddleware extends RateLimitedJobMiddleware {
          public function handle($job, $next) {
              try {
                  parent::handle($job, $next);
              } catch (RateLimitExceededException $e) {
                  Log::warning("Rate limit exceeded for {$job->job}", ['job' => $job]);
                  throw $e; // Or handle differently
              }
          }
      }
      

Gotchas and Tips

Pitfalls

  1. Cache Driver Mismatch:

    • The package defaults to the cache driver. If using a non-shared cache (e.g., local file cache), rate limits won’t sync across workers.
    • Fix: Use Redis or database cache:
      'driver' => env('RATE_LIMIT_CACHE_DRIVER', 'redis'),
      
  2. Key Collisions:

    • Poorly designed keys (e.g., static strings) can lead to unintended rate-limiting groups.
    • Tip: Use unique, context-aware keys (e.g., user_id, tenant_id + action).
  3. Time Window Misconfiguration:

    • Incorrect perMinute/perHour settings may cause unexpected throttling.
    • Debug: Check the spatie/laravel-rate-limiting package (dependency) for detailed logs.
  4. Middleware Order:

    • Place RateLimitedJobMiddleware before other middleware that might modify the job (e.g., Retryable).
    • Example:
      public function middleware() {
          return [
              new \Spatie\RateLimitedJobMiddleware\RateLimitedJobMiddleware(),
              new \Illuminate\Queue\Middleware\Retryable(),
          ];
      }
      
  5. Testing Quirks:

    • Rate limits persist across tests. Reset the cache between tests:
      public function tearDown(): void {
          Artisan::call('cache:clear');
          parent::tearDown();
      }
      

Debugging Tips

  1. Log Rate Limit Checks: Enable debug mode in config/rate-limited-job-middleware.php:

    'debug' => env('RATE_LIMIT_DEBUG', false),
    

    Logs will appear in storage/logs/laravel.log.

  2. Inspect Stored Limits: Dump the cache to verify stored limits:

    \Illuminate\Support\Facades\Cache::get('rate_limits');
    
  3. Override Storage: Extend the package to use a custom storage mechanism (e.g., database):

    use Spatie\RateLimitedJobMiddleware\RateLimit;
    
    class CustomRateLimit extends RateLimit {
        public function __construct() {
            $this->storage = new DatabaseRateLimitStorage();
        }
    }
    

Extension Points

  1. Custom Rate Limit Logic: Extend the RateLimit class to implement dynamic limits:

    class DynamicRateLimit extends \Spatie\RateLimitedJobMiddleware\RateLimit {
        public function allowed($job, $key) {
            if ($job->isHighPriority()) {
                return true; // Bypass limits for high-priority jobs
            }
            return parent::allowed($job, $key);
        }
    }
    
  2. Event-Based Notifications: Listen for rate limit events to trigger alerts:

    RateLimitExceeded::listen(function ($job, $key) {
        Notification::route('mail', 'admin@example.com')
            ->notify(new RateLimitExceededNotification($job, $key));
    });
    
  3. Bulk Job Handling: For batch jobs, use a single key to group multiple dispatches:

    'limits' => [
        'batch-process' => [
            'maxJobs' => 20,
            'perMinute' => true,
            'key' => 'batch_id_123', // Static key for grouped jobs
        ],
    ],
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport