Redis Storage

Production-grade FSM storage using Redis with Pydantic validation, circuit breaker protection, and atomic compare-and-set via WATCH/MULTI/EXEC transactions.

Setup

import redis.asyncio as redis
from pyrogram_patch.fsm import RedisStorage

redis_client = redis.Redis(
    host="localhost",
    port=6379,
    db=0,
    decode_responses=True,
)

storage = RedisStorage(redis_client, prefix="mybot:fsm:")

Constructor

ParameterTypeDefaultDescription
redisredis.asyncio.RedisAsync Redis client instance
prefixstr"pyrogram_patch:"Key prefix for all FSM entries

Circuit Breaker Integration

Every Redis operation is wrapped in a circuit breaker. If Redis becomes unavailable, the breaker opens and prevents flooding a dead connection. Once Redis recovers, the breaker transitions through HALF_OPEN back to CLOSED automatically.

CLOSEDfailure_threshold →OPENtimeout →HALF_OPENsuccess →CLOSED

Configure via PyrogramPatchConfig.circuit_breaker (environment variables or code):

# Environment variables:
# PYROGRAM_PATCH_CIRCUIT_BREAKER__FAILURE_THRESHOLD=5
# PYROGRAM_PATCH_CIRCUIT_BREAKER__SUCCESS_THRESHOLD=2
# PYROGRAM_PATCH_CIRCUIT_BREAKER__TIMEOUT=30
# PYROGRAM_PATCH_CIRCUIT_BREAKER__FALLBACK_MESSAGE="Service temporarily unavailable"

Pydantic Validation

State data is validated at write time using FSMStateModel (a Pydantic model). Invalid payloads are rejected before touching Redis, preventing corrupt state.

Atomic Compare-and-Set

Uses Redis WATCH/MULTI/EXEC transactions for safe concurrent updates. The compare_and_set() method reads the current state, verifies it matches the expected value, then atomically writes the new value — all inside a transaction. If another client modifies the key between WATCH and EXEC, the transaction fails and returns False.

# Used internally by RateLimitMiddleware for safe counter increments
success = await storage.compare_and_set(
    id="ratelimit:user:12345:1700000",
    new_state={"count": 4},
    expected_state={"count": 3},
    ttl=60,
)