redis-store
Skill0 starsChanged 3 months agoMITView on GitHub
Master Redis for caching, session storage, rate limiting, pub/sub, and distributed data structures with proper expiration and persistence
- Installs packages
In this repository27 other files it gives its agents.
- architecture-designerSkill
- architecture-patternsSkill
- code-reviewSkill
- dockerSkill
- fastapiSkill
- feature-specificationSkill
- github-issue-resolverSkill
- github-opsSkill
- kubernetes-devopsSkill
- monitoringSkill
- nextjsSkill
- nginxSkill
- prd-to-ddd-designSkill
- prd-writerSkill
- prismaSkill
- react-expertSkill
- redisSkill
- requirements-analysisSkill
- shadcn-uiSkill
- ssh-essentialsSkill
- task-decomposerSkill
- task-development-workflowSkill
- trpc-best-practicesSkill
- typescript-proSkill
- typescriptSkill
- webhookSkill
- zodSkill
The file
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 4 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 26
Use this skill when:
- Distinctive: line 37
Don't use: when you need:
- Very specific: line 86
Karpathy Principle: Simplicity First: - Start with basic SET/GET.
- Very specific: line 221
Karpathy Principle: Goal-Driven Execution: - After storing data, verify with redis-cli: GET key, HGETALL user:1, ZRANGE leaderboard 0 -1 …
- Distinctive: line 500
Before deploying Redis in production:
- Distinctive: line 585
Karpathy Principle: Simplicity First: - Start with simple key-value caching.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Redis - In-Memory Data Store
When to Use
Use this skill when:
- Caching database queries or API responses
- Session storage for web applications
- Rate limiting and throttling
- Real-time analytics and counters
- Pub/sub messaging between services
- Job queues (with Redis Streams)
- Leaderboards and rankings (sorted sets)
- Distributed locks and coordination
Don't use when you need:
- Complex queries with JOINs (use SQL database)
- Long-term persistent storage (Redis is primarily in-memory)
- ACID transactions across multiple keys (limited support)
- Multi-gigabyte datasets on small memory machines
Karpathy Principle: Think Before Coding - Redis is RAM-based. Every key costs memory. Plan your data structures, expiration policies, and eviction strategies before storing data. A memory leak in Redis crashes your application.
Prerequisites
Required Knowledge
- Key-value store concepts
- Data structure basics (lists, sets, hashes, sorted sets)
- TTL and expiration strategies
- Pub/sub patterns
Required Tools
# Install Redis (Ubuntu/Debian)
sudo apt update
sudo apt install redis-server
# Install Redis (macOS)
brew install redis
# Start Redis
sudo systemctl start redis
# or
redis-server
# Test connection
redis-cli ping # Should return PONG
# Monitor commands in real-time
redis-cli monitorClient Libraries
# Node.js
npm install redis ioredis
# Python
pip install redis
# Go
go get github.com/go-redis/redis/v8Karpathy Principle: Simplicity First - Start with basic SET/GET. Add complexity (sorted sets, streams, Lua scripts) only when needed. Most caching needs are solved with strings and hashes.
Core Workflows
1. Basic Operations (Strings)
Node.js (ioredis):
import Redis from 'ioredis'
const redis = new Redis({
host: 'localhost',
port: 6379,
maxRetriesPerRequest: 3,
})
// Set with expiration
await redis.setex('session:123', 3600, JSON.stringify({ user: 'alice' }))
// Get
const session = await redis.get('session:123')
const data = JSON.parse(session)
// Set with options (Redis 6.2+)
await redis.set('key', 'value', 'EX', 3600, 'NX') // Only if not exists
// Atomic increment
await redis.incr('page:views')
await redis.incrby('user:score', 10)
// Delete
await redis.del('session:123')
// Check existence
const exists = await redis.exists('key') // Returns 1 if exists, 0 if notPython:
import redis
import json
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
# Set with expiration
r.setex('session:123', 3600, json.dumps({'user': 'alice'}))
# Get
session = r.get('session:123')
data = json.loads(session) if session else None
# Increment
r.incr('page:views')
# Delete
r.delete('session:123')2. Data Structures
Hashes (for objects):
// Set multiple fields
await redis.hset('user:1', {
name: 'Alice',
email: 'alice@example.com',
age: 30,
})
// Get single field
const name = await redis.hget('user:1', 'name')
// Get all fields
const user = await redis.hgetall('user:1')
// { name: 'Alice', email: 'alice@example.com', age: '30' }
// Increment field
await redis.hincrby('user:1', 'age', 1)
// Delete field
await redis.hdel('user:1', 'email')Lists (queues, recent items):
// Push to queue
await redis.lpush('queue:jobs', JSON.stringify({ id: 1, task: 'send email' }))
// Pop from queue (blocking)
const job = await redis.brpop('queue:jobs', 5) // Wait 5 seconds max
if (job) {
const [key, value] = job
const data = JSON.parse(value)
}
// Recent items
await redis.lpush('recent:posts', 'post:123')
await redis.ltrim('recent:posts', 0, 99) // Keep only 100 most recentSets (unique items):
// Add to set
await redis.sadd('tags:typescript', 'post:1', 'post:2', 'post:3')
// Check membership
const isMember = await redis.sismember('tags:typescript', 'post:1')
// Get all members
const posts = await redis.smembers('tags:typescript')
// Set operations
await redis.sinter('tags:typescript', 'tags:nodejs') // Intersection
await redis.sunion('tags:typescript', 'tags:python') // UnionSorted Sets (leaderboards, rankings):
// Add with score
await redis.zadd('leaderboard', 100, 'alice', 85, 'bob', 92, 'charlie')
// Get top N
const top3 = await redis.zrevrange('leaderboard', 0, 2, 'WITHSCORES')
// ['alice', '100', 'charlie', '92', 'bob', '85']
// Get rank (0-based)
const rank = await redis.zrevrank('leaderboard', 'alice') // 0 (first place)
// Increment score
await redis.zincrby('leaderboard', 5, 'bob') // bob: 85 → 90
// Get score
const score = await redis.zscore('leaderboard', 'alice') // '100'Karpathy Principle: Goal-Driven Execution - After storing data, verify with redis-cli: GET key, HGETALL user:1, ZRANGE leaderboard 0 -1 WITHSCORES. Don't trust your code until you see the data.
3. Caching Patterns
Cache-Aside:
async function getUser(userId) {
const cacheKey = `user:${userId}`
// Try cache first
const cached = await redis.get(cacheKey)
if (cached) return JSON.parse(cached)
// Cache miss - fetch from DB
const user = await db.user.findUnique({ where: { id: userId } })
// Store in cache (1 hour)
await redis.setex(cacheKey, 3600, JSON.stringify(user))
return user
}Write-Through:
async function updateUser(userId, data) {
// Update DB
const user = await db.user.update({
where: { id: userId },
data,
})
// Update cache
await redis.setex(`user:${userId}`, 3600, JSON.stringify(user))
return user
}Cache Invalidation:
async function deleteUser(userId) {
// Delete from DB
await db.user.delete({ where: { id: userId } })
// Delete from cache
await redis.del(`user:${userId}`)
}4. Rate Limiting
Fixed Window:
async function checkRateLimit(userId, limit = 100) {
const key = `rate:${userId}:${Math.floor(Date.now() / 60000)}` // Per minute
const current = await redis.incr(key)
if (current === 1) {
await redis.expire(key, 60) // Set TTL on first request
}
return current <= limit
}Sliding Window (more accurate):
async function checkRateLimitSliding(userId, limit = 100, windowSeconds = 60) {
const key = `rate:${userId}`
const now = Date.now()
const windowStart = now - windowSeconds * 1000
// Remove old entries
await redis.zremrangebyscore(key, 0, windowStart)
// Count requests in window
const count = await redis.zcard(key)
if (count < limit) {
await redis.zadd(key, now, `${now}:${Math.random()}`)
await redis.expire(key, windowSeconds)
return true
}
return false
}Common Patterns
1. Session Storage
// Create session
const sessionId = crypto.randomUUID()
await redis.setex(`session:${sessionId}`, 86400, JSON.stringify({
userId: '123',
role: 'admin',
createdAt: Date.now(),
}))
// Get session
const session = await redis.get(`session:${sessionId}`)
// Extend session
await redis.expire(`session:${sessionId}`, 86400)
// Delete session (logout)
await redis.del(`session:${sessionId}`)2. Distributed Lock
async function acquireLock(resource, token, ttl = 30) {
const key = `lock:${resource}`
const result = await redis.set(key, token, 'NX', 'EX', ttl)
return result === 'OK'
}
async function releaseLock(resource, token) {
const script = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`
return await redis.eval(script, 1, `lock:${resource}`, token)
}
// Usage
const token = crypto.randomUUID()
if (await acquireLock('payment:123', token)) {
try {
// Do work
} finally {
await releaseLock('payment:123', token)
}
}3. Pub/Sub Messaging
// Publisher
await redis.publish('notifications', JSON.stringify({
type: 'new_message',
userId: '123',
}))
// Subscriber
const subscriber = new Redis()
await subscriber.subscribe('notifications')
subscriber.on('message', (channel, message) => {
const data = JSON.parse(message)
console.log(`Received on ${channel}:`, data)
})4. Job Queue (with Streams)
// Producer
await redis.xadd('queue:emails', '*', 'to', 'alice@example.com', 'subject', 'Hello')
// Consumer
while (true) {
const messages = await redis.xreadgroup(
'GROUP', 'workers', 'worker-1',
'BLOCK', 5000,
'STREAMS', 'queue:emails', '>'
)
if (messages) {
for (const [stream, entries] of messages) {
for (const [id, fields] of entries) {
// Process job
const job = Object.fromEntries(
fields.reduce((acc, val, i, arr) => {
if (i % 2 === 0) acc.push([val, arr[i + 1]])
return acc
}, [])
)
// Acknowledge
await redis.xack('queue:emails', 'workers', id)
}
}
}
}Common Pitfalls
1. No TTL (Memory Leak)
❌ Bad (no expiration):
await redis.set('cache:data', data) // Lives forever!✅ Good (with TTL):
await redis.setex('cache:data', 3600, data) // Expires in 1 hour2. Large Keys
❌ Bad (1MB+ values):
await redis.set('user:sessions', JSON.stringify(allSessions)) // Huge value✅ Good (split into small keys):
for (const session of sessions) {
await redis.setex(`session:${session.id}`, 3600, JSON.stringify(session))
}3. Blocking Operations
❌ Bad (blocks Redis):
const keys = await redis.keys('user:*') // Scans entire keyspace!✅ Good (non-blocking):
let cursor = '0'
const keys = []
do {
const [nextCursor, batch] = await redis.scan(cursor, 'MATCH', 'user:*', 'COUNT', 100)
keys.push(...batch)
cursor = nextCursor
} while (cursor !== '0')4. No maxmemory Limit
❌ Bad (no memory limit):
# redis.conf
# (no maxmemory set)✅ Good (with eviction policy):
# redis.conf
maxmemory 1gb
maxmemory-policy allkeys-lru # Evict least recently used5. Forgetting Connection Pooling
❌ Bad (new connection per request):
app.get('/api/user', async (req, res) => {
const redis = new Redis() // New connection!
const user = await redis.get('user:1')
await redis.quit()
})✅ Good (reuse connection):
const redis = new Redis() // Outside handler
app.get('/api/user', async (req, res) => {
const user = await redis.get('user:1')
})Verification Checklist
Before deploying Redis in production:
Configuration
maxmemoryset (e.g., 1gb, 2gb)maxmemory-policyconfigured (allkeys-lru for cache)- Persistence enabled (RDB or AOF or both)
bindaddress restricted (not 0.0.0.0 for public)- Password set (
requirepass)
Operations
- All cache keys have TTL
- No blocking commands in production (
KEYS,FLUSHALL) - Connection pooling enabled
- Monitoring set up (memory, CPU, evictions)
Performance
- Pipeline batch operations
- Use hashes for small objects (memory efficient)
- Avoid storing large values (>1MB)
- Index patterns for
SCANoperations
Integration with Other Skills
With Node.js / Express
- Session middleware with redis-store
- Rate limiting middleware
- Cache API responses
With Next.js
- Cache server-side rendered pages
- Session storage
- Real-time updates via pub/sub
With Docker
- Redis container in docker-compose
- Volume for persistence
- Health checks
With Kubernetes
- Redis Sentinel for HA
- Redis Cluster for sharding
- Persistent volume claims
References
Official Docs
Guides
Tools
- redis-cli - Command-line client
- RedisInsight - GUI client
Meta: Skill Quality
Completeness: ✅ Comprehensive (9/9 sections)
Karpathy Principles: ✅ 5 mentions
Code Examples: ✅ 25+ working examples
Production Tested: ✅ Used in NorthRelay, MemoryRelay
Last Updated: 2026-04-13
Coverage:
- ✅ Basic operations (strings, hashes, lists, sets, sorted sets)
- ✅ Caching patterns (cache-aside, write-through)
- ✅ Rate limiting (fixed window, sliding window)
- ✅ Session storage
- ✅ Distributed locks
- ✅ Pub/sub messaging
- ✅ Job queues (Streams)
- ✅ Memory management
- ✅ Common pitfalls
Skill Level: Intermediate
Time to Learn: 3-4 hours
Prerequisites Met: Key-value concepts, basic programming
Known Gaps: None - production-ready
Karpathy Principle: Cache Invalidation is Hard - The two hardest problems in CS: naming things, cache invalidation, off-by-one errors. Always have explicit invalidation strategies.
Karpathy Principle: Simplicity First - Start with simple key-value caching. Add complex patterns (pub/sub, streams) only when simple patterns fail.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

