redis
SKILL.md
Redis Core Knowledge
Deep Knowledge: Use
mcp__documentation__fetch_docswith technology:redisfor comprehensive documentation.
Data Types
Strings
SET user:1:name "John"
GET user:1:name
SETEX session:abc 3600 "user_data" # Expires in 1h
INCR page:views
Hashes
HSET user:1 name "John" email "john@example.com"
HGET user:1 name
HGETALL user:1
HINCRBY user:1 loginCount 1
Lists
LPUSH notifications:1 "New message"
RPUSH queue:jobs '{"task": "send_email"}'
LRANGE notifications:1 0 9
LPOP queue:jobs
Sets
SADD user:1:roles "admin" "editor"
SISMEMBER user:1:roles "admin"
SMEMBERS user:1:roles
SINTER user:1:friends user:2:friends # Common friends
Sorted Sets
ZADD leaderboard 100 "user:1" 200 "user:2"
ZRANGE leaderboard 0 9 REV WITHSCORES # Top 10
ZINCRBY leaderboard 10 "user:1"
Common Patterns
Caching
async function getUser(id) {
const cached = await redis.get(`user:${id}`);
if (cached) return JSON.parse(cached);
const user = await db.users.find(id);
await redis.setex(`user:${id}`, 3600, JSON.stringify(user));
return user;
}
Rate Limiting
async function rateLimit(ip) {
const key = `ratelimit:${ip}`;
const count = await redis.incr(key);
if (count === 1) await redis.expire(key, 60);
return count <= 100;
}
Production Readiness
Connection Configuration
// redis.ts
import Redis from 'ioredis';
const redis = new Redis({
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT || '6379'),
password: process.env.REDIS_PASSWORD,
db: parseInt(process.env.REDIS_DB || '0'),
// Connection pool
maxRetriesPerRequest: 3,
retryStrategy: (times) => {
if (times > 10) return null; // Stop retrying
return Math.min(times * 100, 3000);
},
// TLS for production
tls: process.env.NODE_ENV === 'production' ? {} : undefined,
// Keep-alive
keepAlive: 30000,
connectTimeout: 10000,
});
redis.on('error', (err) => {
console.error('Redis connection error:', err);
});
redis.on('connect', () => {
console.log('Redis connected');
});
export { redis };
Cache Patterns
// Generalized cache-aside pattern
async function cacheAside<T>(
key: string,
ttl: number,
fetcher: () => Promise<T>
): Promise<T> {
const cached = await redis.get(key);
if (cached) {
return JSON.parse(cached);
}
const data = await fetcher();
await redis.setex(key, ttl, JSON.stringify(data));
return data;
}
// Cache invalidation
async function invalidatePattern(pattern: string): Promise<void> {
const keys = await redis.keys(pattern);
if (keys.length > 0) {
await redis.del(...keys);
}
}
// Example usage
const user = await cacheAside(
`user:${id}`,
3600, // 1 hour TTL
() => db.users.findUnique({ where: { id } })
);
// On user update
await invalidatePattern(`user:${id}*`);
Rate Limiting (Sliding Window)
async function slidingWindowRateLimit(
key: string,
limit: number,
windowSeconds: number
): Promise<{ allowed: boolean; remaining: number }> {
const now = Date.now();
const windowStart = now - windowSeconds * 1000;
const multi = redis.multi();
multi.zremrangebyscore(key, 0, windowStart);
multi.zadd(key, now, `${now}-${Math.random()}`);
multi.zcard(key);
multi.expire(key, windowSeconds);
const results = await multi.exec();
const count = results?.[2]?.[1] as number;
return {
allowed: count <= limit,
remaining: Math.max(0, limit - count),
};
}
Session Storage
interface SessionData {
userId: string;
roles: string[];
createdAt: number;
}
async function createSession(userId: string, roles: string[]): Promise<string> {
const sessionId = crypto.randomUUID();
const session: SessionData = {
userId,
roles,
createdAt: Date.now(),
};
await redis.setex(
`session:${sessionId}`,
86400, // 24 hours
JSON.stringify(session)
);
return sessionId;
}
async function getSession(sessionId: string): Promise<SessionData | null> {
const data = await redis.get(`session:${sessionId}`);
return data ? JSON.parse(data) : null;
}
async function deleteSession(sessionId: string): Promise<void> {
await redis.del(`session:${sessionId}`);
}
Distributed Locking
async function acquireLock(
resource: string,
ttlMs: number
): Promise<string | null> {
const lockId = crypto.randomUUID();
const result = await redis.set(
`lock:${resource}`,
lockId,
'PX',
ttlMs,
'NX'
);
return result === 'OK' ? lockId : null;
}
async function releaseLock(resource: string, lockId: string): Promise<boolean> {
const script = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`;
const result = await redis.eval(script, 1, `lock:${resource}`, lockId);
return result === 1;
}
Monitoring
// Health check
async function healthCheck(): Promise<{ status: string; latency: number }> {
const start = Date.now();
await redis.ping();
return {
status: 'healthy',
latency: Date.now() - start,
};
}
// Key metrics
async function getMetrics() {
const info = await redis.info('stats');
const memory = await redis.info('memory');
return { info, memory };
}
Monitoring Metrics
| Metric | Target |
|---|---|
| Connection pool usage | < 80% |
| Cache hit ratio | > 90% |
| Latency (p99) | < 5ms |
| Memory usage | < 80% max |
Checklist
- Connection pooling configured
- TLS enabled in production
- Retry strategy with backoff
- Password authentication
- Key expiration on all cached data
- Cache invalidation strategy
- Rate limiting implemented
- Distributed locking for critical sections
- Health check endpoint
- Memory monitoring alerts
When NOT to Use This Skill
- Relational data - Use
postgresqlormysqlfor structured data with relationships - Document storage - Use
mongodbfor complex document structures - Full-text search - Use
elasticsearchfor search indexing and analytics - Primary database - Redis is for caching/sessions, not as main data store
- Large objects - Store references in Redis, data in object storage
Anti-Patterns
| Anti-Pattern | Problem | Solution |
|---|---|---|
| No TTL on keys | Memory leak, unbounded growth | Always set expiration with SETEX or EXPIRE |
| Storing large objects | Performance degradation, memory pressure | Keep values small (<100KB), use compression |
| Using KEYS in production | Blocks server, O(N) operation | Use SCAN for iteration |
| No connection pooling | Connection exhaustion | Configure pool (ioredis, node-redis) |
| Ignoring eviction policy | Random data loss when full | Set appropriate policy (allkeys-lru) |
| Single Redis instance | Single point of failure | Use Redis Cluster or Sentinel |
| No password in production | Security vulnerability | Always configure AUTH |
Quick Troubleshooting
| Problem | Diagnostic | Fix |
|---|---|---|
| Memory full | INFO memory, MEMORY STATS |
Increase maxmemory, set eviction policy |
| Slow responses | SLOWLOG GET 10 |
Optimize queries, use pipelining |
| Connection refused | Check maxclients limit |
Increase limit or fix connection leaks |
| High latency | redis-cli --latency |
Check network, enable keep-alive |
| Keys not expiring | TTL key returns -1 |
Set TTL on keys, check PERSIST calls |
| Replication lag | INFO replication |
Check network, reduce write load |
Reference Documentation
Weekly Installs
12
Repository
claude-dev-suit…ev-suiteGitHub Stars
2
First Seen
12 days ago
Security Audits
Installed on
opencode11
github-copilot11
codex11
kimi-cli11
gemini-cli11
amp11