<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tan Nguyen Phuong</title>
    <description>The latest articles on DEV Community by Tan Nguyen Phuong (@tannp).</description>
    <link>https://dev.to/tannp</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F396532%2F0fbbaa46-df11-45ad-8d5a-3694eab5d321.jpeg</url>
      <title>DEV Community: Tan Nguyen Phuong</title>
      <link>https://dev.to/tannp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tannp"/>
    <language>en</language>
    <item>
      <title>Distributed Locking with Redis: Understanding the Redlock Algorithm</title>
      <dc:creator>Tan Nguyen Phuong</dc:creator>
      <pubDate>Mon, 24 Aug 2026 04:42:31 +0000</pubDate>
      <link>https://dev.to/tannp/distributed-locking-with-redis-understanding-the-redlock-algorithm-36na</link>
      <guid>https://dev.to/tannp/distributed-locking-with-redis-understanding-the-redlock-algorithm-36na</guid>
      <description>&lt;h1&gt;
  
  
  Distributed Locking with Redis: Understanding the Redlock Algorithm
&lt;/h1&gt;

&lt;p&gt;A single Redis instance makes a fine lock for a single-process app, but the moment you have multiple services racing to acquire the same lock — and that Redis instance can fail — a naive &lt;code&gt;SETNX&lt;/code&gt; isn't safe anymore. &lt;strong&gt;Redlock&lt;/strong&gt;, proposed by Redis's creator, is an algorithm for acquiring a distributed lock across multiple independent Redis nodes so that no single node's failure or slowness can silently break mutual exclusion.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is genuinely advanced material — understanding it requires comfort with distributed systems failure modes, not just Redis commands.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why a Single Redis Lock Isn't Enough
&lt;/h2&gt;

&lt;p&gt;A basic lock looks simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;SET resource:order-42 my-random-value NX PX 30000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sets the key only if it doesn't exist (&lt;code&gt;NX&lt;/code&gt;), with a 30-second expiry (&lt;code&gt;PX&lt;/code&gt;) so a crashed client doesn't hold the lock forever. The problem is that this single Redis node is a single point of failure. If it goes down before replicating the write to a replica, and the replica gets promoted to master, another client can acquire the "same" lock — because the new master never saw the key.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Redlock Algorithm
&lt;/h2&gt;

&lt;p&gt;Redlock solves this by using &lt;strong&gt;N independent Redis masters&lt;/strong&gt; (the reference implementation uses 5), with no replication or coordination between them. A client wanting the lock does the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get the current time in milliseconds.&lt;/li&gt;
&lt;li&gt;Try to acquire the lock on all N instances sequentially, using the same key and a random value, with a small timeout per instance so a down node doesn't stall the whole process.&lt;/li&gt;
&lt;li&gt;Compute elapsed time. The lock is considered acquired only if the client got the lock on a &lt;strong&gt;majority&lt;/strong&gt; (N/2 + 1) of instances, and the total elapsed time is less than the lock's validity time.&lt;/li&gt;
&lt;li&gt;If acquired, the effective validity time is the original TTL minus the elapsed time and clock drift.&lt;/li&gt;
&lt;li&gt;If the lock wasn't acquired, release it on every instance immediately, whether or not that instance thought it succeeded.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph TD
    A[Client wants lock] --&amp;gt; B[Record start time T1]
    B --&amp;gt; C[Try SET NX PX on Redis Node 1]
    B --&amp;gt; D[Try SET NX PX on Redis Node 2]
    B --&amp;gt; E[Try SET NX PX on Redis Node 3]
    B --&amp;gt; F[Try SET NX PX on Redis Node 4]
    B --&amp;gt; G[Try SET NX PX on Redis Node 5]
    C --&amp;gt; H{Count successes}
    D --&amp;gt; H
    E --&amp;gt; H
    F --&amp;gt; H
    G --&amp;gt; H
    H --&amp;gt;|Majority acquired AND time OK| I[Lock acquired]
    H --&amp;gt;|Majority failed OR time expired| J[Release lock on all nodes]
    J --&amp;gt; K[Retry after random backoff]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why Majority Matters
&lt;/h2&gt;

&lt;p&gt;Requiring a majority (not all N) means Redlock tolerates the failure of a minority of nodes — with 5 nodes, up to 2 can be down or unreachable and the lock still works correctly, the same fault-tolerance principle behind Raft and Paxos-based systems. It also prevents split-brain: two clients can't both get a majority of 5 nodes simultaneously, because any two majorities of 5 must overlap by at least one node.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Random Value and Safe Release
&lt;/h2&gt;

&lt;p&gt;Each client generates a unique random value for its lock attempt (not just any placeholder). Releasing the lock must check that the stored value still matches before deleting it, done atomically via a Lua script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;KEYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"del"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;KEYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without this check, a client could accidentally delete a lock it no longer owns — for example, if its own lock expired and a different client acquired it in the meantime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clock Drift: The Real Weakness
&lt;/h2&gt;

&lt;p&gt;Redlock's correctness assumes clocks across the N nodes don't drift too far apart relative to the lock's TTL. If a node's clock jumps forward unexpectedly (a bad NTP sync, a VM pause-and-resume, manual clock changes), that node might expire a lock much earlier than the others believe, opening a window where two clients think they hold the lock simultaneously. This is the core of Martin Kleppmann's well-known critique of Redlock: it depends on real-time clock behavior in ways that are hard to fully guarantee.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fencing Tokens: The Practical Fix
&lt;/h2&gt;

&lt;p&gt;Even with Redlock done correctly, a paused client (GC pause, VM suspend, network partition) can wake up after its lock has expired and still act as if it holds it — writing to a shared resource after another client has already acquired the lock and moved on. The standard mitigation is a &lt;strong&gt;fencing token&lt;/strong&gt;: a monotonically increasing number returned every time a lock is granted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client A acquires lock, gets token 33
Client A pauses (GC, network delay...)
Client A's lock expires
Client B acquires lock, gets token 34
Client B writes to storage, tagging the write with token 34
Client A wakes up, tries to write with token 33
Storage rejects it: 33 &amp;lt; 34 (already saw a higher token)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The protected resource itself — a database, a file store, an API — must be the one enforcing the token check. Redlock alone cannot make this guarantee; it can only reduce the &lt;em&gt;likelihood&lt;/em&gt; of two clients believing they hold the lock at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Redlock (and When Not To)
&lt;/h2&gt;

&lt;p&gt;Redlock is a reasonable choice for &lt;strong&gt;efficiency locks&lt;/strong&gt; — cases where a duplicate execution is wasteful but not catastrophic, like preventing the same cron-triggered job from running twice, or coalescing duplicate cache-rebuild requests. It is a risky choice for &lt;strong&gt;correctness locks&lt;/strong&gt; — cases where a duplicate execution corrupts data, like coordinating writes to a financial ledger. For those, use a system with strong consistency guarantees (a consensus-based lock service like ZooKeeper or etcd) combined with fencing tokens enforced by the resource itself, not just Redis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Pitfalls
&lt;/h2&gt;

&lt;p&gt;Using an even number of nodes defeats the majority-quorum logic that makes Redlock resilient — always use an odd number so a majority is unambiguous. Setting per-node acquisition timeouts too high means a single slow node can blow past your lock's TTL before you've even finished the acquisition phase. And treating Redlock as a strict correctness guarantee, rather than a best-effort mutual exclusion mechanism, is the mistake that leads to production incidents — pair it with fencing tokens whenever the operation it protects actually matters.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Redlock reduces the probability of a broken lock; it does not eliminate it. Design your protected operations to survive the case where the lock briefly fails.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://cslant.com/tips/distributed-locking-with-redis-understanding-the-redlock-algorithm" rel="noopener noreferrer"&gt;https://cslant.com/tips/distributed-locking-with-redis-understanding-the-redlock-algorithm&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>redis</category>
      <category>distributedsystems</category>
    </item>
  </channel>
</rss>
