Download the PHP package largerio/laravel-concurrent-limiter without Composer
On this page you can find all versions of the php package largerio/laravel-concurrent-limiter. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download largerio/laravel-concurrent-limiter
More information about largerio/laravel-concurrent-limiter
Files in largerio/laravel-concurrent-limiter
Package laravel-concurrent-limiter
Short Description LaravelConcurrentLimiter is a lightweight Laravel middleware that limits concurrent requests per user (or IP). It delays requests until a slot is available or returns an error after a maximum wait time, protecting your app from resource exhaustion and ensuring fair usage under high load.
License MIT
Homepage https://github.com/largerio/laravel-concurrent-limiter
Informations about the package laravel-concurrent-limiter
Laravel Concurrent Limiter
A Laravel middleware package that limits the number of concurrent requests per user (or IP). Unlike rate limiting which counts requests over time, this package controls how many requests can be processed simultaneously.
Features
- HTTP Middleware - Limit concurrent requests per user/IP with automatic queuing
- Job Middleware - Limit concurrent queue job execution to protect external APIs
- Adaptive Limiting - Auto-adjust limits based on latency using AIMD algorithm
- Prometheus Metrics - Built-in
/metricsendpoint for monitoring - Fail-safe - Configurable behavior when cache is unavailable
- Events - Full request lifecycle tracking (wait, acquire, release, exceed)
- Extensible - Custom key resolvers and response handlers
Requirements
- PHP 8.3 or higher
- Laravel 11.x or 12.x
- Cache store with atomic operations (Redis recommended)
Table of Contents
- Quick Start
- Installation
- HTTP Middleware
- Job Middleware
- Configuration
- Adaptive Limiting
- Events
- Custom Key Resolver
- Custom Response Handler
- Prometheus Metrics
- Cache
- Artisan Commands
- Troubleshooting
- License
Quick Start
This limits each user to 5 concurrent requests, waiting up to 30 seconds for a slot before returning 503.
Installation
Install via Composer:
The service provider is auto-discovered. To publish the config file:
HTTP Middleware
Apply the middleware to routes using the concurrent.limit alias:
Or use the static helper:
How it works:
- Generates a unique key based on user ID (or IP if unauthenticated)
- Increments a counter in cache
- If over limit, waits (polling every 100ms) until a slot is free
- If timeout reached, returns 503 with JSON error
- After processing, decrements the counter
Job Middleware
Limit concurrent execution of queued jobs to protect external APIs or shared resources:
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
maxParallel |
int | 5 | Maximum concurrent jobs |
key |
string | 'default' | Identifier for grouping jobs |
releaseAfter |
int | 30 | Seconds before retrying |
shouldRelease |
bool | true | Release job back to queue if limited |
Use Cases:
- Limit API calls to third-party services (Stripe, Twilio, etc.)
- Prevent database overload from batch processing
- Control concurrent file processing or exports
Configuration
| Option | Default | Description |
|---|---|---|
max_parallel |
10 | Maximum concurrent requests per user |
max_wait_time |
30 | Seconds to wait before returning 503 |
ttl_buffer |
60 | Extra TTL seconds for cache safety |
cache_prefix |
concurrent-limiter: |
Cache key prefix |
cache_store |
null | Cache store (null = default) |
error_message |
"Too many concurrent..." | 503 response message |
retry_after |
true | Include Retry-After header |
key_resolver |
null | Custom KeyResolver class |
response_handler |
null | Custom ResponseHandler class |
on_cache_failure |
'allow' | Behavior on cache failure: 'allow' or 'reject' |
logging.enabled |
false | Log when limits are exceeded |
logging.channel |
null | Log channel (null = default) |
logging.level |
'warning' | Log level |
metrics.enabled |
false | Enable Prometheus metrics endpoint |
metrics.route |
'/concurrent-limiter/metrics' | Metrics endpoint path |
metrics.middleware |
[] | Middleware for metrics endpoint |
adaptive.enabled |
false | Enable adaptive concurrency limiting |
adaptive.algorithm |
'vegas' | Algorithm: 'vegas' or 'gradient2' |
adaptive.min_limit |
1 | Minimum concurrency limit |
adaptive.max_limit |
100 | Maximum concurrency limit |
adaptive.ewma_alpha |
0.3 | EWMA smoothing factor (Vegas) |
adaptive.sample_window |
60 | Metrics TTL in seconds |
adaptive.min_rtt_reset_samples |
1000 | Reset minRTT after N samples (Vegas) |
adaptive.rtt_tolerance |
2.0 | Acceptable latency multiplier (Gradient2) |
Adaptive Limiting
Automatically adjust maxParallel based on observed response latency using algorithms inspired by Netflix's concurrency-limits library.
Available Algorithms
Vegas (default) - Based on TCP Vegas congestion control:
- Tracks minimum RTT (best-case latency) as baseline
- Compares current latency to baseline to detect queueing
- Uses dynamic alpha/beta thresholds based on current limit
- Best for: Server-side protection, proactive congestion detection
Gradient2 - Based on EWMA divergence:
- Tracks short-term and long-term EWMA
- Detects latency trends by comparing the two averages
- Configurable tolerance for latency increase
- Best for: Detecting gradual degradation, noisy environments
Enable Adaptive Limiting
How Adaptive Interacts with maxParallel
When adaptive limiting is enabled, the maxParallel parameter from your route acts as a hard cap:
| Scenario | maxParallel | Adaptive calculates | Effective limit |
|---|---|---|---|
| Good latency | 10 | 15 | 10 (capped) |
| High latency | 10 | 3 | 3 (reduced) |
| No metrics yet | 10 | 10 | 10 (initial) |
This ensures that adaptive limiting is a safety optimization - it can reduce load when latency degrades, but never allows more concurrent requests than you explicitly configured.
Vegas Algorithm Details
Formula:
Example: With limit=10, minRTT=100ms, avgRTT=100ms:
- gradient = 1.0, queueUse = 0
- 0 < alpha (1) → increase to 11
Gradient2 Algorithm Details
Formula:
Example: With tolerance=2.0, shortEWMA=200ms, longEWMA=100ms:
- gradient = 0.5, threshold = 0.5
- 0.5 >= 0.5 → stable (just within tolerance)
Use Cases
- Auto-scaling protection: Automatically reduce concurrency when backend is overloaded
- Variable workloads: Handle traffic spikes without manual tuning
- Proactive detection: Vegas detects congestion before timeouts occur
Monitoring
Access metrics programmatically:
Events
The middleware dispatches events for monitoring and logging:
| Event | When | Properties |
|---|---|---|
ConcurrentLimitWaitStarted |
Request starts waiting for a slot | $request, $currentCount, $maxParallel, $key |
ConcurrentLimitAcquired |
Request acquires a slot | $request, $waitedSeconds, $key |
ConcurrentLimitExceeded |
Timeout reached, returning 503 | $request, $waitedSeconds, $maxParallel, $key |
ConcurrentLimitReleased |
Request completed | $request, $totalTime, $key |
CacheOperationFailed |
Cache operation fails | $request (nullable), $exception |
Example listener:
Custom Key Resolver
By default, the middleware uses the authenticated user ID or IP address. Implement KeyResolver to customize:
Register in config:
Custom Response Handler
Customize the 503 response by implementing ResponseHandler:
Register in config:
Prometheus Metrics
Enable Prometheus-compatible metrics for monitoring:
Available Metrics:
| Metric | Type | Description |
|---|---|---|
concurrent_limiter_requests_total |
Counter | Total requests processed |
concurrent_limiter_exceeded_total |
Counter | Requests rejected (503) |
concurrent_limiter_cache_failures_total |
Counter | Cache operation failures |
concurrent_limiter_wait_seconds |
Histogram | Time spent waiting for slots |
Example Output:
Grafana Tips:
- Alert on
rate(concurrent_limiter_exceeded_total[5m]) > 10 - Monitor p99 wait time with histogram quantiles
- Track cache failures for infrastructure issues
Cache
Store Recommendations
The middleware requires a cache store that supports atomic operations:
| Cache Store | Production Ready | Notes |
|---|---|---|
| Redis | Yes | Best choice. Supports locks for atomic operations. |
| Memcached | Yes | Good alternative to Redis. |
| DynamoDB | Yes | Works with Laravel DynamoDB cache driver. |
| Database | Limited | Works but may cause contention under high load. |
| File | No | No locking support. Race conditions possible. |
| Array | No | Only for testing. Data lost between requests. |
Configure in config/concurrent-limiter.php:
Key Structure
| Context | Pattern | Example |
|---|---|---|
| HTTP requests | {prefix}{custom_prefix}{user_id\|ip_hash} |
concurrent-limiter:api:abc123 |
| Job queue | {prefix}job:{key} |
concurrent-limiter:job:stripe-api |
| Locks | {key}:lock |
concurrent-limiter:api:abc123:lock |
Failure Handling
By default, if cache is unavailable, requests are allowed through (fail-open). For critical endpoints:
| Mode | Behavior | Use Case |
|---|---|---|
allow |
Let requests through | General APIs, non-critical endpoints |
reject |
Return 503 error | Payment processing, rate-sensitive operations |
Artisan Commands
Check Counter Status
Clear Stuck Counters
Troubleshooting
Always getting 503 errors
- Check
maxParallelsetting - It might be too low for your traffic - Verify cache is working - Test with
Cache::put('test', 1); Cache::get('test'); - Check for stuck counters - They expire after
maxWaitTime + ttl_bufferseconds
Requests not being limited
- Verify middleware is applied - Run
php artisan route:list - Check cache store -
arraydriver doesn't persist between requests - Different users/IPs - Each user/IP has their own limit
Performance issues
- Use Redis - Fastest option with proper locking support
- Reduce
maxWaitTime- Lower wait times free up resources faster - Tune
maxParallel- Balance between protection and throughput
Debugging
Enable logging to see when limits are exceeded:
Changelog
See CHANGELOG.md for release history.
License
This package is open-sourced software licensed under the MIT license.
All versions of laravel-concurrent-limiter with dependencies
spatie/laravel-package-tools Version ^1.16
illuminate/contracts Version ^11.0||^12.0