Skip to content
Distributed Locking with Redlock: Coordinating Across Nodes 3 min

Distributed Locking with Redlock: Coordinating Across Nodes

SD
ScaleDojo
May 11, 2026
3 min read610 words
Distributed Locking with Redlock: Coordinating Across Nodes

The Double-Charge Nightmare

You have 5 instances of your payment service behind a load balancer. A webhook arrives from Stripe saying payment for order #123 completed. Due to a retry, the webhook hits two instances simultaneously. Both check the database - order #123 is unpaid. Both process the payment. Customer gets charged twice. Refund, apology, trust lost. A distributed lock prevents this.

Simple Redis Lock

Redis Single-Instance Lock:

  # Acquire lock
  SET lock:order:123 worker-7a NX EX 30
  #     key             value    only-if-    expires in
  #                              not-exists  30 seconds

  # If SET returns OK   -> you have the lock
  # If SET returns nil  -> someone else has it, back off

  # Do your work...
  process_payment(order_123)

  # Release lock (only if you still own it!)
  # Use Lua script for atomicity:
  if redis.call('GET', 'lock:order:123') == 'worker-7a' then
      return redis.call('DEL', 'lock:order:123')
  end

  Why check ownership before delete?
  - Your lock might have EXPIRED while you were working
  - Another worker acquired it
  - If you blindly DEL, you release THEIR lock
  - Now two workers are in the critical section

Redlock: Fault-Tolerant Distributed Lock

A single Redis instance is a single point of failure. Redlock uses N independent Redis instances (typically 5) for redundancy.

Distributed Locking with Redlock: Coordinating Across Nodes - Architecture Diagram
Architecture overview
Redlock Algorithm (N=5 Redis instances):

  1. Record current time T1
  2. Try to acquire lock on all 5 instances sequentially
     (with short per-instance timeout, e.g., 5-10ms)
  3. Record current time T2

  Lock acquired if:
  [a] Got lock on majority (3+ of 5 instances)
  [b] Total time (T2-T1) < lock TTL

  Effective TTL = original TTL - (T2-T1)

  If lock NOT acquired:
  Release on ALL 5 instances (even successful ones)

  Example timeline:
  Instance 1: SET OK  (2ms)
  Instance 2: SET OK  (3ms)
  Instance 3: SET OK  (1ms)   -> 3/5 = majority!
  Instance 4: TIMEOUT (10ms)  -> skip
  Instance 5: SET OK  (2ms)

  Total: 18ms. TTL was 30s. Effective TTL: 29.982s
  Lock acquired! Can tolerate 2 instance failures.

The Kleppmann vs Antirez Debate

In 2016, Martin Kleppmann (author of Designing Data-Intensive Applications) published a famous critique of Redlock. The Redis creator (Antirez) responded. This is one of the most educational debates in distributed systems.

Kleppmann's concern - GC Pause Breaks the Lock:

  Time 0s:     Worker A acquires lock (TTL=30s)
  Time 0.001s: Worker A's JVM enters GC pause...
  Time 31s:    Lock expires! Worker A is still paused.
  Time 31s:    Worker B acquires lock legitimately.
  Time 35s:    Worker A wakes up, thinks it has lock.
  Time 35s:    BOTH workers in critical section!

  Kleppmann's fix: Use 'fencing tokens'
  - Lock service issues monotonically increasing token
  - Storage layer rejects writes with old tokens
  - Even if stale worker wakes up, its token is rejected

  The lesson: Distributed locks provide BEST-EFFORT
  mutual exclusion, not absolute safety.
  For true safety, use fencing tokens or consensus systems.

When to Use What

Need                     Solution             Notes
-----------------------  -------------------  ----------------------
Efficiency lock           Redis SET NX         Duplicate work is OK,
(avoid duplicate work)                         just wasteful
Correctness lock          ZooKeeper / etcd     Consensus-based, safe
(double-charge = bad)     with fencing tokens  against GC pauses
Simple leader election    etcd lease           Built for this
Distributed rate limit    Redis + Lua          Not a lock, but similar

Interview Tip

When discussing distributed locks, say: 'For efficiency locks - where duplicates are wasteful but not catastrophic - I would use Redis SET NX with an expiration. For correctness locks - like payment processing where duplicates cause real harm - I would use a consensus-based system like etcd or ZooKeeper with fencing tokens. The key insight from the Kleppmann-Antirez debate is that Redis locks can be broken by GC pauses or clock drift, so they provide best-effort, not absolute mutual exclusion.'

Key Takeaway

Distributed locks coordinate exclusive access across multiple service instances. Redis SET NX is simple. Redlock adds fault tolerance with multiple Redis nodes. For non-critical coordination, Redis locks work well. For critical financial operations, use a proper consensus system like etcd or ZooKeeper with fencing tokens.

Enjoyed this article?

Share it with your network to help others level up their system design skills.

Discussion0

Join the Discussion

Sign in to leave comments, reply to others, or like insights.

Sign In to ScaleDojo

No comments yet. Be the first to start the thread!

Related Articles

Enjoyed this content?

Your support keeps us creating free resources

We put a lot of hours into researching and writing these guides. If it helped you, consider buying us a coffee. Every bit goes toward keeping ScaleDojo's content free and growing.

$

One-time payment via Stripe. ScaleDojo account required.

ScaleDojo Logo
Initializing ScaleDojo