Course contentsShow
← Node.js
Lesson 260 of 1,9803. Asynchronous Patterns and Flow ControlFree lesson

Distributed Async Locking

You will implement a distributed lock mechanism using Redis to coordinate asynchronous state across multiple instances.

In this lesson, you will learn how to synchronize asynchronous tasks across multiple Node.js instances using a centralized "Distributed Lock."

The Problem: Multi-Node Race Conditions

When you run your application on a single server, you can manage concurrency with local variables or memory-based queues. However, when you scale to multiple nodes, each instance has its own memory. If two nodes attempt to process the same "once-only" task (like generating a monthly financial report or updating a shared database row), they might trigger it simultaneously. This leads to duplicate work or data corruption.

The Solution: Redis as the Arbiter

To coordinate these nodes, we use an external "Source of Truth"—typically Redis. A Distributed Lock is essentially a temporary key stored in Redis that acts as a "Stop" sign.

  1. Acquire: Before starting a task, a node attempts to create a key in Redis (e.g., process_lock) with an expiration time. If the key doesn't exist, the node successfully sets it and proceeds.
  2. Block: If the key already exists, other nodes know someone else is currently handling the task and must wait (or abort).
  3. Release: Once the task finishes, the node deletes the key, allowing others to compete for the lock next time.

Implementation Concept

Using a library like ioredis, the logic looks like this:

async function performExclusiveTask(redis, lockKey) {
  // SET with NX (Only set if not exists) and PX (Expiration in ms)
  const isLocked = await redis.set(lockKey, 'locked', 'PX', 5000, 'NX');

  if (!isLocked) {
    console.log("Task is already being processed by another instance.");
    return;
  }

  try {
    await runImportantTask(); // Your async work
  } finally {
    await redis.del(lockKey); // Release lock
  }
}

The PX 5000 is crucial: it ensures that if a node crashes mid-task, the lock automatically expires after 5 seconds, preventing a permanent "deadlock" where no node can ever run the task again. By offloading this coordination to Redis, you ensure that even across a fleet of containers, your asynchronous flows remain atomic and orderly.

Key Takeaway: Distributed locking uses an external, atomic shared state to ensure that only one node in a cluster executes a specific asynchronous task at any given time.