Node.js › Caching with Redis

How would you implement a distributed lock using Redis to coordinate access to a shared resource across multiple Node.js instances?

Advanced

Answer

A distributed lock uses Redis's atomic SET with the NX (only set if not exists) and EX (expiration) options to acquire a lock -- only one instance can successfully set the key, guaranteeing mutual exclusion, with the expiration ensuring the lock is automatically released even if the instance holding it crashes without explicitly releasing it, preventing a permanently stuck lock.
async function acquireLock(resource, ttlMs = 10000) {
  const lockId = crypto.randomUUID();
  const acquired = await client.set(`lock:${resource}`, lockId, { NX: true, PX: ttlMs });
  return acquired ? lockId : null;
}

async function releaseLock(resource, lockId) {
  // Only release if we still hold the lock (using a Lua script for atomicity)
  const script = `if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end`;
  await client.eval(script, { keys: [`lock:${resource}`], arguments: [lockId] });
}
Real-world example A daily report-generation job that must run exactly once, even though it's triggered from multiple redundant scheduler instances for high availability, uses a Redis distributed lock so only the first instance to acquire the lock actually runs the job, while the others detect the lock is held and skip execution.

Common follow-ups: Why is using a unique lockId and checking it before deletion (via a Lua script) important, rather than just calling DEL directly on release?;What are the known limitations of this simple locking approach compared to the more rigorous Redlock algorithm across multiple Redis nodes?