Communitygithub.com

redis-patterns

Modèles de structures de données Redis, stratégies de cache, verrous distribués, limitation de débit, pub/sub et gestion des connexions pour les applications de production. À utiliser lors de l'ajout de cache, d'un verrou distribué, de limitation de débit ou de pub/sub avec Redis, ou lorsque la conception des clés a besoin d'être revue.

Qu'est-ce que redis-patterns ?

Ce skill fournit un guide complet des bonnes pratiques Redis pour les cas d'usage backend courants. Il couvre les structures de données fondamentales (chaînes, hachages, listes, ensembles, ensembles triés, flux) et des motifs clés tels que le cache-aside, le cache write-through, l'invalidation par tags et le stockage de sessions. Pour la limitation de débit, il explique les approches fenêtre fixe (simple) et fenêtre glissante (atomique via Lua), cette dernière étant plus précise. Les verrous distribués sont implémentés avec SET NX PX et le script Lua de libération, avec une mention de redlock-py pour les environnements multi-nœuds. Le skill aborde également la gestion des connexions (pool, cluster, sentinel), les politiques d'éviction, les anti-patrons comme l'absence de TTL ou l'utilisation de KEYS *, et la prévention de l'effet de troupeau. Il est compatible avec la bibliothèque Redis en Python (via redis-py) et d'autres langages, et nécessite une instance Redis en cours d'exécution.

Compatible avec~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/redis-patterns

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Redis Patterns

Référence rapide des bonnes pratiques Redis pour les cas d'usage backend courants.

Comment ça fonctionne

Redis est un magasin de données en mémoire qui prend en charge les chaînes, les hachages, les listes, les ensembles, les ensembles triés, les flux et plus encore. Les commandes Redis individuelles sont atomiques sur une seule instance ; les workflows en plusieurs étapes nécessitent des scripts Lua, des transactions MULTI/EXEC ou une synchronisation explicite pour rester atomiques. Les données sont éventuellement persistées via des instantanés RDB ou des journaux AOF. Les clients communiquent via TCP en utilisant le protocole RESP ; les pools de connexions sont essentiels pour éviter la surcharge de négociation par requête.

Quand activer

  • Ajouter un cache à une application
  • Mettre en œuvre la limitation de débit ou le throttling
  • Construire des verrous distribués ou de la coordination
  • Mettre en place un stockage de sessions ou de jetons
  • Utiliser Pub/Sub ou Redis Streams pour la messagerie
  • Configurer Redis en production (pooling, éviction, clustering)

Aide-mémoire des structures de données

Cas d'usageStructureExemple de clé
Cache simpleStringproduct:123
Session utilisateurHashsession:abc
ClassementSorted Setscores:weekly
Visiteurs uniquesSetvisitors:2024-01-01
Flux d'activitéListfeed:user:456
Flux d'événementsStreamevents:orders
Compteurs / limites de débitString (INCR)ratelimit:user:123
Filtre de Bloom / HLLHyperLogLoghll:pageviews

Motifs principaux

Cache-Aside (Chargement paresseux)

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 heure
    return product

Cache Write-Through

def update_product(product_id: int, data: dict):
    # Écrire d'abord dans la base
    db.execute("UPDATE products SET ... WHERE id = %s", product_id)

    # Mettre à jour le cache immédiatement
    cache_key = f"product:{product_id}"
    r.setex(cache_key, 3600, json.dumps(data))

Invalidation de cache

# Invalidation par tag — regrouper les clés associées sous un ensemble
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)

Stockage de sessions

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}")

Limitation de débit

Fenêtre fixe (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

Fenêtre glissante (Lua — Atomique)

-- 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
    -- Utiliser un membre unique (now + séquence) pour éviter les collisions dans la même milliseconde
    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]))

Verrous distribués

Verrou distribué (nœud unique — 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)

Pour les configurations multi-nœuds, utilisez la bibliothèque redlock-py qui implémente l'algorithme Redlock complet.

Pub/Sub & Streams

Pub/Sub (Fire-and-Forget)

# Éditeur
def publish_event(channel: str, payload: dict):
    r.publish(channel, json.dumps(payload))

# Abonné (bloquant — exécuter dans un thread/processus séparé)
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 (File d'attente durable)

# Producteur
def emit(stream: str, event: dict):
    r.xadd(stream, event, maxlen=10000)  # Limiter la longueur du flux

# Groupe de consommateurs — garantie de livraison au moins une fois
try:
    r.xgroup_create('events:orders', 'processor', id='0', mkstream=True)
except Exception:
    pass  # Le groupe existe déjà

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)

Préférez les Streams à Pub/Sub lorsque vous avez besoin de garanties de livraison, de groupes de consommateurs ou de rejeu.

Conception des clés

Conventions de nommage

# Motif : ressource:id:champ
user:123:profile
order:456:status
cache:product:789

# Motif : espace de noms:ressource:id
myapp:session:abc123
myapp:ratelimit:user:123

# Motif : ressource:date (clés limitées dans le temps)
stats:pageviews:2024-01-01

Stratégie TTL

Type de donnéesTTL suggéré
Session utilisateur24h (86400)
Cache de réponse API5–15 min
Fenêtre de limitation de débitCorrespondre à la taille de la fenêtre
Jetons éphémères5–10 min
Classement1h–24h
Données statiques/de référence1h–1 semaine

Définissez toujours un TTL. Les clés sans TTL s'accumulent indéfiniment et causent une pression mémoire.

Gestion des connexions

Pool de connexions

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)

Mode cluster

from redis.cluster import RedisCluster

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

Sentinel (Haute disponibilité)

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)

Politiques d'éviction

PolitiqueComportementMeilleur pour
noevictionErreur en écriture quand pleinFiles d'attente / données critiques
allkeys-lruÉviction du moins récemment utiliséCache général
volatile-lruLRU uniquement parmi les clés avec TTLMagasin de données mixte
allkeys-lfuÉviction du moins fréquemment utiliséModèles d'accès asymétriques
volatile-ttlÉviction du plus proche de l'expirationPrioriser les données à longue durée de vie

Définir via redis.conf : maxmemory-policy allkeys-lru

Anti-patrons

Anti-patronProblèmeCorrectif
Clés sans TTLLa mémoire croît indéfinimentToujours définir un TTL
KEYS * en productionBloque le serveur (O(N))Utiliser le curseur SCAN
Stocker de gros blobs (>100 Ko)Sérialisation lente, pression mémoireStocker une référence + récupérer depuis un stockage d'objets
Utiliser Redis pour toutPas d'isolation entre cache & file d'attenteUtiliser des bases ou instances séparées
Ignorer les limites du pool de connexionsÉpuisement des connexions sous chargeDimensionner le pool pour la charge de travail
Ne pas gérer la ruée sur l'absence de cacheEffet de troupeau au démarrage à froidUtiliser des verrous ou une expiration précoce probabiliste
FLUSHALL sans réflexionEfface toute l'instanceLimiter les suppressions par motif de clé

Prévention de la ruée sur l'absence de cache

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)  # Revérifier après avoir acquis le verrou
        if cached:
            return json.loads(cached)
        value = fetch_fn()
        r.setex(key, ttl, json.dumps(value))
        return value

Remarque : pour les déploiements multi-processus, remplacez le verrou intra-processus par acquire_lock/release_lock de la section Verrous distribués ci-dessus.

Exemples

Ajouter un cache à un point de terminaison API Django/Flask : Utilisez le cache-aside avec setex et un TTL de 5 minutes sur la réponse. Clé sur les paramètres de la requête.

Limiter le débit d'une API par utilisateur : Utilisez la fenêtre fixe avec pipeline(transaction=True) pour les points de terminaison à faible trafic ; utilisez le Lua à fenêtre glissante pour un throttling précis par utilisateur.

Coordonner un travail en arrière-plan entre plusieurs workers : Utilisez acquire_lock avec un TTL qui dépasse la durée prévue du travail. Libérez toujours dans un bloc finally.

Diffuser des notifications à plusieurs abonnés : Utilisez Pub/Sub pour le fire-and-forget. Passez aux Streams si vous avez besoin d'une livraison garantie ou d'un rejeu pour les consommateurs tardifs.

Référence rapide

MotifQuand l'utiliser
Cache-asideLecture intensive, tolère une légère obsolescence
Write-throughCohérence forte requise
Verrou distribuéEmpêcher l'accès concurrent à une ressource
Limitation de débit fenêtre glissanteThrottling précis par utilisateur
Redis StreamsFile d'attente d'événements durable avec groupes de consommateurs
Pub/SubDiffusion sans garantie de livraison nécessaire
Classement par ensemble triéNotation classée, pagination
HyperLogLogComptage approximatif unique à faible mémoire

Liens connexes

  • Skill : postgres-patterns — motifs de données relationnelles
  • Skill : backend-patterns — motifs de couche API et service
  • Skill : database-migrations — versionnement de schéma
  • Skill : django-patterns — intégration du cache Django
  • Agent : database-reviewer — processus complet de révision de base de données

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

Skills associés