Loading skill
Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Data structure selection, pub/sub patterns, Lua scripting, pipelining, and cluster topology strategies.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-14 | ✗→✓ | ▲ Improved | 181% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 80% | 0% |
| case-11 | ✓→✗ | ▼ Worse | 149% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 45% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 39% | 0% |
Advanced Redis usage beyond simple key-value caching.
Use Case → Structure → Why
Session store → Hash → Partial field updates without full deserialize
Rate limiter → Sorted Set → ZRANGEBYSCORE for sliding window
Job queue → List + Stream → BRPOPLPUSH for reliable queue
Leaderboard → Sorted Set → ZREVRANGE with scores
Unique visitors → HyperLogLog → O(1) cardinality, 0.81% error
Feature flags → Hash → HGET per flag, HGETALL for bulk
Presence (who's online) → Set → SADD/SREM, SMEMBERS
Geolocation → Geo → GEOSEARCH within radiustypescriptimport Redis from 'ioredis' const redis = new Redis(process.env.REDIS_URL!) async function acquireLock( key: string, ttlMs: number = 10_000 ): Promise<string | null> { const token = crypto.randomUUID() const acquired = await redis.set( `lock:${key}`, token, 'PX', ttlMs, 'NX' // Only set if not exists ) return acquired === 'OK' ? token : null } async function releaseLock(key: string, token: string): Promise<boolean> { // Lua script: only delete if token matches (atomic check-and-delete) 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:${key}`, token) return result === 1 } // Usage async function processExclusively(resourceId: string): Promise<void> { const token = await acquireLock(resourceId, 5000) if (!token) throw new Error('Could not acquire lock') try { await doWork(resourceId) } finally { await releaseLock(resourceId, token) } }
typescriptasync function isRateLimited( userId: string, maxRequests: number, windowMs: number ): Promise<boolean> { const key = `ratelimit:${userId}` const now = Date.now() const windowStart = now - windowMs const pipeline = redis.pipeline() pipeline.zremrangebyscore(key, 0, windowStart) // Remove expired entries pipeline.zadd(key, now.toString(), `${now}:${crypto.randomUUID()}`) pipeline.zcard(key) // Count entries in window pipeline.pexpire(key, windowMs) // Auto-cleanup const results = await pipeline.exec() const count = results![2][1] as number return count > maxRequests }
typescript// Problem: check-then-act is not atomic with separate commands // Solution: Lua script executes atomically on Redis server // Atomic counter with ceiling const incrementWithCeiling = ` local current = tonumber(redis.call("GET", KEYS[1]) or "0") local ceiling = tonumber(ARGV[1]) if current >= ceiling then return -1 end return redis.call("INCR", KEYS[1]) ` async function tryIncrement(key: string, max: number): Promise<number> { const result = await redis.eval(incrementWithCeiling, 1, key, max.toString()) return result as number // -1 means ceiling reached } // Atomic dequeue with conditional processing const dequeueIfReady = ` local item = redis.call("LPOP", KEYS[1]) if item == nil then return nil end local data = cjson.decode(item) if data.scheduledAt and tonumber(data.scheduledAt) > tonumber(ARGV[1]) then redis.call("LPUSH", KEYS[1], item) -- Put back, not ready yet return nil end redis.call("SADD", KEYS[2], item) -- Move to processing set return item `
typescript// Without pipeline: N round trips // With pipeline: 1 round trip for N commands async function bulkGetUsers(userIds: string[]): Promise<Map<string, User>> { const pipeline = redis.pipeline() for (const id of userIds) { pipeline.hgetall(`user:${id}`) } const results = await pipeline.exec() const userMap = new Map<string, User>() for (let i = 0; i < userIds.length; i++) { const [err, data] = results![i] if (!err && data && Object.keys(data as object).length > 0) { userMap.set(userIds[i], data as User) } } return userMap }
typescript// Streams > Pub/Sub: persistence, consumer groups, acknowledgment // Producer async function publishEvent(stream: string, event: Record<string, string>): Promise<string> { // MAXLEN ~ 10000: approximate trimming for performance return redis.xadd(stream, 'MAXLEN', '~', '10000', '*', ...Object.entries(event).flat()) } // Consumer group setup await redis.xgroup('CREATE', 'events', 'worker-group', '0', 'MKSTREAM') // Consumer async function consumeEvents( stream: string, group: string, consumer: string ): Promise<void> { while (true) { const entries = await redis.xreadgroup( 'GROUP', group, consumer, 'COUNT', '10', 'BLOCK', '5000', // Block up to 5s for new messages 'STREAMS', stream, '>' ) if (!entries) continue for (const [, messages] of entries) { for (const [id, fields] of messages) { try { await processEvent(Object.fromEntries( fields.reduce((acc: [string, string][], v, i, arr) => i % 2 === 0 ? [...acc, [v, arr[i + 1]]] : acc, [] ) )) await redis.xack(stream, group, id) } catch (err) { console.error(`Failed to process ${id}:`, err) // Message stays pending, will be re-delivered } } } } }
SLOWLOG GET 10Other measured skills in the registry, with their headline benchmark lift.