mirror of
http://github.com/valkey-io/valkey
synced 2024-11-22 00:52:38 +00:00
52 lines
5.2 KiB
HTML
52 lines
5.2 KiB
HTML
|
|
||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||
|
<html>
|
||
|
<head>
|
||
|
<link type="text/css" rel="stylesheet" href="style.css" />
|
||
|
</head>
|
||
|
<body>
|
||
|
<div id="page">
|
||
|
|
||
|
<div id='header'>
|
||
|
<a href="index.html">
|
||
|
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||
|
</a>
|
||
|
</div>
|
||
|
|
||
|
<div id="pagecontent">
|
||
|
<div class="index">
|
||
|
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||
|
<b>SetnxCommand: Contents</b><br> <a href="#SETNX _key_ _value_">SETNX _key_ _value_</a><br> <a href="#Return value">Return value</a><br> <a href="#Design pattern: Implementing locking with SETNX">Design pattern: Implementing locking with SETNX</a><br> <a href="#Handling deadlocks">Handling deadlocks</a>
|
||
|
</div>
|
||
|
|
||
|
<h1 class="wikiname">SetnxCommand</h1>
|
||
|
|
||
|
<div class="summary">
|
||
|
|
||
|
</div>
|
||
|
|
||
|
<div class="narrow">
|
||
|
#sidebar <a href="StringCommandsSidebar.html">StringCommandsSidebar</a><h1><a name="SETNX _key_ _value_">SETNX _key_ _value_</a></h1>
|
||
|
<i>Time complexity: O(1)</i><blockquote>SETNX works exactly like <a href="SetCommand.html">SET</a> with the only difference thatif the key already exists no operation is performed.SETNX actually means "SET if Not eXists".</blockquote>
|
||
|
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>, specifically:<br/><br/><pre class="codeblock python" name="code">
|
||
|
1 if the key was set
|
||
|
0 if the key was not set
|
||
|
</pre><h2><a name="Design pattern: Implementing locking with SETNX">Design pattern: Implementing locking with SETNX</a></h2><blockquote>SETNX can also be seen as a locking primitive. For instance to acquirethe lock of the key <b>foo</b>, the client could try the following:</blockquote>
|
||
|
<pre class="codeblock python python" name="code">
|
||
|
SETNX lock.foo <current UNIX time + lock timeout + 1>
|
||
|
</pre><blockquote>If SETNX returns 1 the client acquired the lock, setting the <b>lock.foo</b>key to the UNIX time at witch the lock should no longer be considered valid.The client will later use <b>DEL lock.foo</b> in order to release the lock.</blockquote>
|
||
|
<blockquote>If SETNX returns 0 the key is already locked by some other client. We caneither return to the caller if it's a non blocking lock, or enter aloop retrying to hold the lock until we succeed or some kind of timeoutexpires.</blockquote>
|
||
|
<h3><a name="Handling deadlocks">Handling deadlocks</a></h3><blockquote>In the above locking algorithm there is a problem: what happens if a clientfails, crashes, or is otherwise not able to release the lock?It's possible to detect this condition because the lock key contains aUNIX timestamp. If such a timestamp is <= the current Unix time the lockis no longer valid.</blockquote>
|
||
|
<blockquote>When this happens we can't just call DEL against the key to remove the lockand then try to issue a SETNX, as there is a race condition here, whenmultiple clients detected an expired lock and are trying to release it.</blockquote>
|
||
|
<ul><li> C1 and C2 read lock.foo to check the timestamp, because SETNX returned 0 to both C1 and C2, as the lock is still hold by C3 that crashed after holding the lock.</li><li> C1 sends DEL lock.foo</li><li> C1 sends SETNX => success!</li><li> C2 sends DEL lock.foo</li><li> C2 sends SETNX => success!</li><li> ERROR: both C1 and C2 acquired the lock because of the race condition.</li></ul>
|
||
|
<blockquote>Fortunately it's possible to avoid this issue using the following algorithm.Let's see how C4, our sane client, uses the good algorithm:</blockquote>
|
||
|
<ul><li> C4 sends SETNX lock.foo in order to acquire the lock</li><li> The crashed C3 client still holds it, so Redis will reply with 0 to C4.</li><li> C4 GET lock.foo to check if the lock expired. If not it will sleep one second (for instance) and retry from the start.</li><li> If instead the lock is expired because the UNIX time at lock.foo is older than the current UNIX time, C4 tries to perform GETSET lock.foo <current unix timestamp + lock timeout + 1></li><li> Thanks to the <a href="GetsetCommand.html">GETSET</a> command semantic C4 can check if the old value stored at key is still an expired timestamp. If so we acquired the lock!</li><li> Otherwise if another client, for instance C5, was faster than C4 and acquired the lock with the GETSET operation, C4 GETSET operation will return a non expired timestamp. C4 will simply restart from the first step. Note that even if C4 set the key a bit a few seconds in the future this is not a problem.</li></ul>
|
||
|
IMPORTANT NOTE: In order to make this locking algorithm more robust, a client holding a lock should always check the timeout didn't expired before to unlock the key with DEL because client failures can be complex, not just crashing but also blocking a lot of time against some operation and trying to issue DEL after a lot of time (when the LOCK is already hold by some other client).
|
||
|
</div>
|
||
|
|
||
|
</div>
|
||
|
</div>
|
||
|
</body>
|
||
|
</html>
|
||
|
|