---
trigger: model_decision
description: Apply when code reads or writes Redis/Valkey: caching, sessions, rate limits, queues, or pub/sub.
---

# Redis / Valkey

## Keys
- Namespaced, colon-separated keys with a version: `app:v1:user:{id}:profile`. Keep the scheme in one `keys.ts`/`keys.py` module.
- Every cache key has a TTL (`SET key val EX 300`). Add jitter to TTLs to avoid stampedes on expiry.
- Never use `KEYS *` in app code; use `SCAN` with `MATCH` for maintenance only.

## Patterns
- Cache-aside: read cache, on miss load from the DB and set with TTL. Invalidate (delete) on write, don't update in place. The database stays the source of truth.
- Rate limiting: atomic `INCR` + `EXPIRE` in a `MULTI` or a Lua script, or a sliding window with sorted sets.
- Locks: `SET lock:x token NX PX 30000` and release only if the token matches (Lua). Prefer avoiding distributed locks.
- Queues: use Streams (`XADD`/`XREADGROUP`/`XACK`) or the job library in use (BullMQ, RQ, Sidekiq) instead of raw lists.
- Pipeline batches of commands; avoid one round trip per item.

## Data
- Store compact JSON or hashes; keep values small (< 100 KB). Don't keep unbounded lists or sets without trimming.
- Don't store secrets or PII without a clear TTL and need.

## Ops
- One shared client with reconnect; set command timeouts. Handle Redis being down by falling back to the DB where possible.
