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

__construct(LockProvider $store, string $name, int $maxLocks, int $releaseAfter)

Create a new Redis-optimized concurrency limiter instance.

acquire(int $timeout, int $sleep = 250)

Acquire a lease on one of the limiter's slots, waiting up to the given timeout.

mixed
block(int $timeout, callable|null $callback = null, int $sleep = 250)

Attempt to acquire the lock for the given number of seconds.

false|Lock
claimSlot(string $id)

Atomically claim a free slot via a single Lua script.

Details

at line 27
__construct(LockProvider $store, string $name, int $maxLocks, int $releaseAfter)

Create a new Redis-optimized concurrency limiter instance.

Parameters

LockProvider $store

the cache store instance

string $name

the name of the limiter

int $maxLocks

the allowed number of concurrent locks

int $releaseAfter

the number of seconds a slot should be maintained

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.

Parameters

int $timeout
int $sleep

Return Value

Lease

Exceptions

LimiterTimeoutException

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.

Parameters

int $timeout
callable|null $callback
int $sleep

Return Value

mixed

Exceptions

LimiterTimeoutException
Throwable

at line 70
protected false|Lock claimSlot(string $id)

Atomically claim a free slot via a single Lua script.

Two correctness invariants:

  1. 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.

  2. 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.

Parameters

string $id

Return Value

false|Lock