Use when implementing Redis patterns for games — caching, leaderboards, pub/sub messaging, session storage, rate limiting, or ephemeral game state. Triggers: Redis, cache, leaderboard, pub/sub, rate limit, session.
Redis usage patterns for games — leaderboards, session caching, pub/sub messaging, rate limiting, ephemeral state, and sorted sets for real-time game data.
Trigger: Redis, pub/sub, leaderboard, session cache, rate limiting, sorted sets, game cache, ephemeral state, TTL, player presence, cross-server messaging
None — this is a foundational infrastructure skill.
Will Wright: "Simulation state must be fast, ephemeral, and disposable — persistence is for outcomes, not for process."
lb:, session:, rl:, presence:) to avoid collisions and enable selective flushingbun add ioredis
Use boilerplate/redis-client.ts for the base connection with reconnection logic and error handling. All other modules import from this file.
Use boilerplate/leaderboard.ts for sorted-set-based leaderboards. Supports adding scores, fetching top N, getting player rank, and fetching nearby ranks. Includes periodic snapshot to PostgreSQL for historical data.
Use boilerplate/session-cache.ts for player session caching with TTL. Follows cache-aside pattern: check cache → miss → load from DB → populate cache.
Use boilerplate/pub-sub.ts for cross-server messaging. Requires separate Redis connections for publishing and subscribing. Typed event handlers ensure type safety.
Use templates/rate-limiter.ts for per-player per-action sliding window rate limiting using sorted sets. Configurable window size and max requests.
Use templates/ephemeral-state.ts for short-lived game state: match state with TTL, player presence via heartbeat, and temporary buffs/effects.
import { leaderboard } from './leaderboard';
await leaderboard.addScore('weekly', 'player_123', 4500);
const top10 = await leaderboard.getTopN('weekly', 10);
const rank = await leaderboard.getPlayerRank('weekly', 'player_123');
import { sessionCache } from './session-cache';
const session = await sessionCache.get('player_123');
if (!session) {
const fromDb = await db.query.players.findFirst({ where: eq(players.id, 'player_123') });
await sessionCache.set('player_123', fromDb, 3600);
}
import { publisher, subscriber } from './pub-sub';
await publisher.publish('match-events', {
type: 'match-started',
matchId: 'match_456',
players: ['player_123', 'player_789'],
});
subscriber.on('match-events', (event) => {
console.log('Match event:', event.type);
});
import { checkRateLimit } from './rate-limiter';
const result = await checkRateLimit('player_123', 'attack', {
maxRequests: 1,
windowSeconds: 5,
});
if (!result.allowed) {
throw new Error(`Rate limited. Retry after ${result.retryAfter}s`);
}
See boilerplate/ for full implementations and templates/ for rate limiting and ephemeral state patterns.
game-backend-architecture for integrating Redis into the game serverbullmq-game-queues for job queues built on Redispostgres-game-schema for persisting leaderboard snapshots and session fallback dataKEYS * in production — it blocks Redis; use SCAN for key enumerationWill Wright (SimCity, The Sims): Simulation state management is about speed and disposability. In SimCity, the game constantly recalculates traffic, power, and zoning — none of that intermediate state needs to survive a restart. Redis serves this exact role in multiplayer games: it holds the hot, fast-changing state that drives the simulation, while the database records outcomes that matter. The moment you treat cache as truth, you've confused process with product.