phpbotgram

RedisEventIsolation extends BaseEventIsolation
in package

FinalYes

Redis-backed FSM event isolation via a distributed SET NX PX lock.

Mirrors aiogram.fsm.storage.redis.RedisEventIsolation (aiogram/fsm/storage/redis.py). Because amphp/redis ^2 does not expose a built-in distributed lock primitive with an async-context-manager interface, this class implements the lock protocol manually:

AcquireSET $lockKey $token NX PX $ttlMs

  • NX ensures only one client sets the key (atomic compare-and-set).
  • PX (millisecond TTL) guarantees the lock auto-expires if the holder crashes, preventing deadlocks.
  • On failure the caller sleeps 50 ms and retries until the TTL budget is consumed, after which a RuntimeException is thrown.

Release — Lua CHECK-AND-DELETE

if redis.call('get', KEYS[1]) == ARGV[1] then
    return redis.call('del', KEYS[1])
else
    return 0
end

This atomically ensures that only the token owner can delete the key, preventing accidental release of a lock acquired by another client after the original lock expired.

The lock is surfaced as a Lock value-object whose release() wires the Lua check-and-delete via an optional $releaseFn closure (Task 5.3 extension).

close() is a no-op: the owning RedisStorage manages the connection lifecycle.

Table of Contents

Constants

RETRY_DELAY_SECONDS  : mixed = 0.05
Poll interval in seconds (50 ms) between acquire retries.
UNLOCK_SCRIPT  : mixed = <<<'LUA' if redis.call('get', KEYS[1]) == ARGV[...
Lua script for safe lock release.

Properties

$acquireTimeoutSeconds  : int|null
$keyBuilder  : KeyBuilder
$lockTtlSeconds  : int
$redis  : RedisClient

Methods

__construct()  : mixed
close()  : void
No-op — the `RedisClient` connection is owned by the storage layer.
lock()  : Lock
Acquire a distributed lock for `$key`.
acquireTimeout()  : int
Resolve the effective acquire-timeout budget in seconds.

Constants

RETRY_DELAY_SECONDS

Poll interval in seconds (50 ms) between acquire retries.

private mixed RETRY_DELAY_SECONDS = 0.05

UNLOCK_SCRIPT

Lua script for safe lock release.

private mixed UNLOCK_SCRIPT = <<<'LUA' if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end LUA

Only deletes the key when the stored value matches the caller's token, preventing a client from releasing a lock it no longer owns (e.g. after TTL expiry and re-acquisition by another client).

Properties

Methods

__construct()

public __construct(RedisClient $redis[, KeyBuilder $keyBuilder = new DefaultKeyBuilder() ][, int $lockTtlSeconds = 60 ][, null|int $acquireTimeoutSeconds = null ]) : mixed
Parameters
$redis : RedisClient

amphp/redis client instance (shared with storage).

$keyBuilder : KeyBuilder = new DefaultKeyBuilder()

Key-builder strategy; defaults to DefaultKeyBuilder.

$lockTtlSeconds : int = 60

Lock TTL in seconds. After this duration the lock auto-expires so a crashed holder cannot block others forever.

$acquireTimeoutSeconds : null|int = null

Maximum wall-clock seconds the acquire loop will spin before giving up with a RuntimeException. Defaults to $lockTtlSeconds * 2 so that a slow holder (still within its TTL) does not starve waiting callers.

close()

No-op — the `RedisClient` connection is owned by the storage layer.

public close() : void

Mirrors RedisEventIsolation.close (upstream redis.py).

lock()

Acquire a distributed lock for `$key`.

public lock(StorageKey $key) : Lock

Spins until SET NX PX succeeds or the acquire-timeout budget is exhausted. Returns a Lock whose release() executes the safe Lua unlock script.

Parameters
$key : StorageKey

Storage address to lock.

Tags
throws
RuntimeException

When the lock cannot be acquired within $acquireTimeoutSeconds (or $lockTtlSeconds * 2 when the former is null).

Return values
Lock

Acquired lock; call release() in a finally block.

acquireTimeout()

Resolve the effective acquire-timeout budget in seconds.

private acquireTimeout() : int

When $acquireTimeoutSeconds is null (the default), falls back to $lockTtlSeconds * 2 so that waiting callers outlast the holder's TTL.

Return values
int
On this page

Search results