RedisConcurrencyLimiter
class RedisConcurrencyLimiter extends ConcurrencyLimiter
Redis-optimized concurrency limiter.
Properties
| protected list<string> | $slots | Precomputed slot names. Built once in the constructor. |
from ConcurrencyLimiter |
| protected list<string> | $prefixedSlots | Precomputed prefixed slot keys for Lua KEYS. Built once in the constructor. |
Methods
Create a new Redis-optimized concurrency limiter instance.
Acquire a lease on one of the limiter's slots, waiting up to the given timeout.
Attempt to acquire the lock for the given number of seconds.
Details
at line 27
__construct(LockProvider $store, string $name, int $maxLocks, int $releaseAfter)
Create a new Redis-optimized concurrency limiter instance.
in
ConcurrencyLimiter at line 51
Lease
acquire(int $timeout, int $sleep = 250)
Acquire a lease on one of the limiter's slots, waiting up to the given timeout.
in
ConcurrencyLimiter at line 84
mixed
block(int $timeout, callable|null $callback = null, int $sleep = 250)
Attempt to acquire the lock for the given number of seconds.
When no callback is given, the slot is reserved fire-and-forget: it is held until the releaseAfter TTL reclaims it. Use acquire() to obtain a releasable lease instead.
at line 70
protected false|Lock
claimSlot(string $id)
Atomically claim a free slot via a single Lua script.
Two correctness invariants:
-
The Lua script writes the prefixed slot key to Redis and returns the UNPREFIXED slot name (e.g. "my-funnel1") so RedisStore::lock() prepends the prefix exactly once when constructing the Lock object.
-
The owner ID must be pre-packed via $connection->pack() before being passed into Lua. phpredis does NOT auto-serialize eval() ARGV (regular commands like set() do). RedisLock::release() later packs $this->owner before its owner-check Lua, so the value Redis stores at acquire time must already be in packed form. If we passed raw $id here, Redis would store the raw string, but release would compare against a packed value
- silent mismatch, slot leaks until TTL. We pass the RAW $id to RedisStore::lock() so the returned RedisLock's owner field is raw; release will pack it consistently with what we stored.
Using withConnection() also keeps both pack() and eval() on the same checked-out pool connection, avoiding two pool roundtrips per attempt.