🔴 Redis Architecture
Redlock Algorithm & ZSET Leaderboards
Preventing race conditions with Redlock and building O(log N) leaderboards with Sorted Sets
In distributed systems, two processes racing to modify shared state cause Race Conditions — inventory goes negative, users get double-charged. Redlock provides a distributed mutex using Redis to ensure only one process holds a lock at a time.
Key Concepts
Redlock Algorithm & Split-Brain
Acquire the same lock key on N/2+1 Redis nodes within a validity window. This quorum approach prevents split-brain scenarios where network partitions cause two clients to both think they hold the lock.
Lock TTL is Critical
Always set a lock expiry (EX 10 seconds). If the lock holder crashes, the lock auto-expires and another process can acquire it. Without expiry, you get permanent deadlocks.
ZSET Leaderboard
ZADD adds a member with score. ZREVRANK returns rank in O(log N). ZREVRANGE returns top-N. A million-user leaderboard query takes microseconds — no SQL ORDER BY needed.
Redlock Quorum and ZSET Leaderboard
Redlock Implementation (Node.js)
flash-sale.service.tstypescript
1import Redlock from 'redlock';
2
3// Initialize with multiple Redis nodes for safety
4const redlock = new Redlock([redis1, redis2, redis3, redis4, redis5]);
5
6async function purchaseLastItem(userId: string, itemId: string) {
7 // Acquire distributed lock — only one winner
8 const lock = await redlock.acquire(
9 [`lock:inventory:${itemId}`],
10 10_000 // 10 second TTL (Very important to prevent deadlocks)
11 );
12
13 try {
14 const stock = await redis.get(`stock:${itemId}`);
15 if (Number(stock) <= 0) throw new Error('Out of stock');
16
17 // Atomic decrement — only after confirming stock > 0
18 await redis.decr(`stock:${itemId}`);
19 await orderService.createOrder(userId, itemId);
20 } finally {
21 // Always release the lock, even on error
22 await lock.release();
23 }
24}
25
26// ── Leaderboard operations (ZSET) ──
27async function updateScore(playerId: string, score: number) {
28 await redis.zadd('leaderboard:global', score, `player:${playerId}`);
29}
30
31async function getTopPlayers(limit = 10) {
32 // ZREVRANGE: Get top scores (descending)
33 return redis.zrevrange('leaderboard:global', 0, limit - 1, 'WITHSCORES');
34}
35
36async function getPlayerRank(playerId: string) {
37 // ZREVRANK returns 0-indexed rank — add 1 for display
38 return redis.zrevrank('leaderboard:global', `player:${playerId}`);
39}💡
Senior Architect Insight: Never use a single Redis instance for Redlock — if it fails, all locks are lost or permanently held. The algorithm requires N Redis instances (typically 5) and a quorum of N/2+1 successful acquisitions to be safe against node failures.