Source profileQuality 90/100

affaan-m/ECC/skills/redis-patterns/SKILL.md

redis-patterns

Redis data structure patterns, caching strategies, distributed locks, rate limiting, pub/sub, and connection management for production applications.

Source repository stars
234,327
Declared platforms
0
Static risk flags
0
Last source update
2026-07-27
Source checked
2026-07-28

Decision brief

What it does—and where it fits

Quick reference for Redis best practices across common backend use cases.

Best for

    Not for

    • Note: for multi-process deployments, replace the in-process lock with acquirelock/releaselock from the Distributed Locks section above.
    • Cache Miss Stampede Prevention

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/affaan-m/ECC --skill "skills/redis-patterns"
    Safe inspection promptEditorial

    Inspect the Agent Skill "redis-patterns" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/skills/redis-patterns/SKILL.md at commit 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      Usage

      token = acquirelock("order:payment:123") if token: try: processpayment() finally: releaselock("order:payment:123", token) python

      token = acquirelock("order:payment:123") if token: try: processpayment() finally: releaselock("order:payment:123", token) python
    2. 02

      Subscriber (blocking — run in separate thread/process)

      def subscribeevents(channel: str): pubsub = r.pubsub() pubsub.subscribe(channel) for message in pubsub.listen(): if message['type'] == 'message': handle(json.loads(message['data'])) python

      def subscribeevents(channel: str): pubsub = r.pubsub() pubsub.subscribe(channel) for message in pubsub.listen(): if message['type'] == 'message': handle(json.loads(message['data'])) python
    3. 03

      How It Works

      Redis is an in-memory data structure store that supports strings, hashes, lists, sets, sorted sets, streams, and more. Individual Redis commands are atomic on a single instance; multi-step workflows require Lua scripts, MULTI/EXEC transactions, or explicit synchronization to sta…

      Redis is an in-memory data structure store that supports strings, hashes, lists, sets, sorted sets, streams, and more. Individual Redis commands are atomic on a single instance; multi-step workflows require Lua scripts,…
    4. 04

      When to Activate

      Adding caching to an application

      Adding caching to an applicationImplementing rate limiting or throttlingBuilding distributed locks or coordination
    5. 05

      Data Structure Cheat Sheet

      Review the “Data Structure Cheat Sheet” section in the pinned source before continuing.

      Review and apply the “Data Structure Cheat Sheet” source section.

    Permission review

    Static risk signals and limitations

    No configured static risk pattern was detected

    This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score90/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars234,327SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    affaan-m/ECC
    Skill path
    skills/redis-patterns/SKILL.md
    Commit
    4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38
    License
    MIT
    Collected
    2026-07-28
    Default branch
    main
    View the original SKILL.md

    Redis Patterns

    Quick reference for Redis best practices across common backend use cases.

    How It Works

    Redis is an in-memory data structure store that supports strings, hashes, lists, sets, sorted sets, streams, and more. Individual Redis commands are atomic on a single instance; multi-step workflows require Lua scripts, MULTI/EXEC transactions, or explicit synchronization to stay atomic. Data is optionally persisted via RDB snapshots or AOF logs. Clients communicate over TCP using the RESP protocol; connection pools are essential to avoid per-request handshake overhead.

    When to Activate

    • Adding caching to an application
    • Implementing rate limiting or throttling
    • Building distributed locks or coordination
    • Setting up session or token storage
    • Using Pub/Sub or Redis Streams for messaging
    • Configuring Redis in production (pooling, eviction, clustering)

    Data Structure Cheat Sheet

    Use CaseStructureExample Key
    Simple cacheStringproduct:123
    User sessionHashsession:abc
    LeaderboardSorted Setscores:weekly
    Unique visitorsSetvisitors:2024-01-01
    Activity feedListfeed:user:456
    Event streamStreamevents:orders
    Counters / rate limitsString (INCR)ratelimit:user:123
    Bloom filter / HLLHyperLogLoghll:pageviews

    Core Patterns

    Cache-Aside (Lazy Loading)

    import redis
    import json
    
    r = redis.Redis(host='localhost', port=6379, decode_responses=True)
    
    def get_product(product_id: int):
        cache_key = f"product:{product_id}"
        cached = r.get(cache_key)
    
        if cached:
            return json.loads(cached)
    
        product = db.query("SELECT * FROM products WHERE id = %s", product_id)
        r.setex(cache_key, 3600, json.dumps(product))  # TTL: 1 hour
        return product
    

    Write-Through Cache

    def update_product(product_id: int, data: dict):
        # Write to DB first
        db.execute("UPDATE products SET ... WHERE id = %s", product_id)
    
        # Immediately update cache
        cache_key = f"product:{product_id}"
        r.setex(cache_key, 3600, json.dumps(data))
    

    Cache Invalidation

    # Tag-based invalidation — group related keys under a set
    def cache_product(product_id: int, category_id: int, data: dict):
        key = f"product:{product_id}"
        tag = f"tag:category:{category_id}"
        pipe = r.pipeline(transaction=True)
        pipe.setex(key, 3600, json.dumps(data))
        pipe.sadd(tag, key)
        pipe.expire(tag, 3600)
        pipe.execute()
    
    def invalidate_category(category_id: int):
        tag = f"tag:category:{category_id}"
        keys = r.smembers(tag)
        if keys:
            r.delete(*keys)
        r.delete(tag)
    

    Session Storage

    import time
    import uuid
    
    def create_session(user_id: int, ttl: int = 86400) -> str:
        session_id = str(uuid.uuid4())
        key = f"session:{session_id}"
        pipe = r.pipeline(transaction=True)
        pipe.hset(key, mapping={
            "user_id": user_id,
            "created_at": int(time.time()),
        })
        pipe.expire(key, ttl)
        pipe.execute()
        return session_id
    
    def get_session(session_id: str) -> dict | None:
        data = r.hgetall(f"session:{session_id}")
        return data if data else None
    
    def delete_session(session_id: str):
        r.delete(f"session:{session_id}")
    

    Rate Limiting

    Fixed Window (Simple)

    def is_rate_limited(user_id: int, limit: int = 100, window: int = 60) -> bool:
        key = f"ratelimit:{user_id}:{int(time.time()) // window}"
        pipe = r.pipeline(transaction=True)
        pipe.incr(key)
        pipe.expire(key, window)
        count, _ = pipe.execute()
        return count > limit
    

    Sliding Window (Lua — Atomic)

    -- sliding_window.lua
    local key = KEYS[1]
    local now = tonumber(ARGV[1])
    local window = tonumber(ARGV[2])
    local limit = tonumber(ARGV[3])
    
    redis.call('ZREMRANGEBYSCORE', key, 0, now - window)
    local count = redis.call('ZCARD', key)
    
    if count < limit then
        -- Use unique member (now + sequence) to avoid collisions within the same millisecond
        local seq_key = key .. ':seq'
        local seq = redis.call('INCR', seq_key)
        redis.call('EXPIRE', seq_key, math.ceil(window / 1000))
        redis.call('ZADD', key, now, now .. '-' .. seq)
        redis.call('EXPIRE', key, math.ceil(window / 1000))
        return 1
    end
    return 0
    
    sliding_window = r.register_script(open('sliding_window.lua').read())
    
    def allow_request(user_id: int) -> bool:
        key = f"ratelimit:sliding:{user_id}"
        now = int(time.time() * 1000)
        return bool(sliding_window(keys=[key], args=[now, 60000, 100]))
    

    Distributed Locks

    Distributed Lock (Single Node — SET NX PX)

    import uuid
    
    def acquire_lock(resource: str, ttl_ms: int = 5000) -> str | None:
        lock_key = f"lock:{resource}"
        token = str(uuid.uuid4())
        acquired = r.set(lock_key, token, px=ttl_ms, nx=True)
        return token if acquired else None
    
    def release_lock(resource: str, token: str) -> bool:
        release_script = """
        if redis.call('get', KEYS[1]) == ARGV[1] then
            return redis.call('del', KEYS[1])
        else
            return 0
        end
        """
        result = r.eval(release_script, 1, f"lock:{resource}", token)
        return bool(result)
    
    # Usage
    token = acquire_lock("order:payment:123")
    if token:
        try:
            process_payment()
        finally:
            release_lock("order:payment:123", token)
    

    For multi-node setups use the redlock-py library which implements the full Redlock algorithm.

    Pub/Sub & Streams

    Pub/Sub (Fire-and-Forget)

    # Publisher
    def publish_event(channel: str, payload: dict):
        r.publish(channel, json.dumps(payload))
    
    # Subscriber (blocking — run in separate thread/process)
    def subscribe_events(channel: str):
        pubsub = r.pubsub()
        pubsub.subscribe(channel)
        for message in pubsub.listen():
            if message['type'] == 'message':
                handle(json.loads(message['data']))
    

    Redis Streams (Durable Queue)

    # Producer
    def emit(stream: str, event: dict):
        r.xadd(stream, event, maxlen=10000)  # Cap stream length
    
    # Consumer group — guarantees at-least-once delivery
    try:
        r.xgroup_create('events:orders', 'processor', id='0', mkstream=True)
    except Exception:
        pass  # Group already exists
    
    def consume(stream: str, group: str, consumer: str):
        while True:
            messages = r.xreadgroup(group, consumer, {stream: '>'}, count=10, block=2000)
            for _, entries in (messages or []):
                for msg_id, data in entries:
                    process(data)
                    r.xack(stream, group, msg_id)
    

    Prefer Streams over Pub/Sub when you need delivery guarantees, consumer groups, or replay.

    Key Design

    Naming Conventions

    # Pattern: resource:id:field
    user:123:profile
    order:456:status
    cache:product:789
    
    # Pattern: namespace:resource:id
    myapp:session:abc123
    myapp:ratelimit:user:123
    
    # Pattern: resource:date (time-bound keys)
    stats:pageviews:2024-01-01
    

    TTL Strategy

    Data TypeSuggested TTL
    User session24h (86400)
    API response cache5–15 min
    Rate limit windowMatch window size
    Short-lived tokens5–10 min
    Leaderboard1h–24h
    Static/reference data1h–1 week

    Always set a TTL. Keys without TTL accumulate indefinitely and cause memory pressure.

    Connection Management

    Connection Pooling

    from redis import ConnectionPool, Redis
    
    pool = ConnectionPool(
        host='localhost',
        port=6379,
        db=0,
        max_connections=20,
        decode_responses=True,
        socket_connect_timeout=2,
        socket_timeout=2,
    )
    
    r = Redis(connection_pool=pool)
    

    Cluster Mode

    from redis.cluster import RedisCluster
    
    r = RedisCluster(
        startup_nodes=[{"host": "redis-1", "port": 6379}],
        decode_responses=True,
        skip_full_coverage_check=True,
    )
    

    Sentinel (High Availability)

    from redis.sentinel import Sentinel
    
    sentinel = Sentinel(
        [('sentinel-1', 26379), ('sentinel-2', 26379)],
        socket_timeout=0.5,
    )
    master = sentinel.master_for('mymaster', decode_responses=True)
    replica = sentinel.slave_for('mymaster', decode_responses=True)
    

    Eviction Policies

    PolicyBehaviorBest For
    noevictionError on write when fullQueues / critical data
    allkeys-lruEvict least recently usedGeneral cache
    volatile-lruLRU only among keys with TTLMixed data store
    allkeys-lfuEvict least frequently usedSkewed access patterns
    volatile-ttlEvict soonest-to-expirePrioritize long-lived data

    Set via redis.conf: maxmemory-policy allkeys-lru

    Anti-Patterns

    Anti-PatternProblemFix
    Keys with no TTLMemory grows unboundedAlways set TTL
    KEYS * in productionBlocks the server (O(N))Use SCAN cursor
    Storing large blobs (>100KB)Slow serialization, memory pressureStore reference + fetch from object store
    Single Redis for everythingNo isolation between cache & queueUse separate DBs or instances
    Ignoring connection pool limitsConnection exhaustion under loadSize pool to workload
    Not handling cache miss stampedeThundering herd on cold startUse locks or probabilistic early expiry
    FLUSHALL without thoughtWipes entire instanceScope deletes by key pattern

    Cache Miss Stampede Prevention

    import threading
    
    _locks: dict[str, threading.Lock] = {}
    _locks_mutex = threading.Lock()
    
    def get_with_lock(key: str, fetch_fn, ttl: int = 300):
        cached = r.get(key)
        if cached:
            return json.loads(cached)
    
        with _locks_mutex:
            if key not in _locks:
                _locks[key] = threading.Lock()
            lock = _locks[key]
        with lock:
            cached = r.get(key)  # Re-check after acquiring lock
            if cached:
                return json.loads(cached)
            value = fetch_fn()
            r.setex(key, ttl, json.dumps(value))
            return value
    

    Note: for multi-process deployments, replace the in-process lock with acquire_lock/release_lock from the Distributed Locks section above.

    Examples

    Add caching to a Django/Flask API endpoint: Use cache-aside with setex and a 5-minute TTL on the response. Key on the request parameters.

    Rate-limit an API by user: Use fixed-window with pipeline(transaction=True) for low-traffic endpoints; use sliding-window Lua for accurate per-user throttling.

    Coordinate a background job across workers: Use acquire_lock with a TTL that exceeds the expected job duration. Always release in a finally block.

    Fan-out notifications to multiple subscribers: Use Pub/Sub for fire-and-forget. Switch to Streams if you need guaranteed delivery or replay for late consumers.

    Quick Reference

    PatternWhen to Use
    Cache-asideRead-heavy, tolerate slight staleness
    Write-throughStrong consistency required
    Distributed lockPrevent concurrent access to a resource
    Sliding window rate limitAccurate per-user throttling
    Redis StreamsDurable event queue with consumer groups
    Pub/SubBroadcast with no delivery guarantees needed
    Sorted Set leaderboardRanked scoring, pagination
    HyperLogLogApproximate unique count at low memory

    Related

    • Skill: postgres-patterns — relational data patterns
    • Skill: backend-patterns — API and service layer patterns
    • Skill: database-migrations — schema versioning
    • Skill: django-patterns — Django cache framework integration
    • Agent: database-reviewer — full database review workflow

    Alternatives

    Compare before choosing

    Computed 84234,327

    affaan-m/ECC

    redis-patterns

    Use it for design tasks; the detail page covers purpose, installation, and practical steps.

    Computed 10042,015

    coreyhaines31/marketingskills

    ab-testing

    When the user wants to plan, design, or implement an A/B test or experiment, or build a growth experimentation program. Also use when the user mentions "A/B test," "split test," "experiment," "test this change," "variant copy," "multivariate test," "hypothesis," "should I test this," "which version is better," "test two versions," "statistical significance," "how long should I run this test," "growth experiments," "experiment velocity," "experiment backlog," "ICE score," "experimentation program

    Computed 10042,015

    coreyhaines31/marketingskills

    churn-prevention

    When the user wants to reduce churn, build cancellation flows, set up save offers, recover failed payments, or implement retention strategies. Also use when the user mentions 'churn,' 'cancel flow,' 'offboarding,' 'save offer,' 'dunning,' 'failed payment recovery,' 'win-back,' 'retention,' 'exit survey,' 'pause subscription,' 'involuntary churn,' 'people keep canceling,' 'churn rate is too high,' 'how do I keep users,' or 'customers are leaving.' Use this whenever someone is losing subscribers o

    Computed 1007

    event4u-app/agent-config

    design-intelligence

    Grounded design brief from the adopted corpus — style, WCAG-checked color tokens, typography, layout pattern, anti-patterns. Use on ui-design-brief or any which-style/palette/font/chart decision.