Commit fc879abe authored by antirez's avatar antirez
Browse files

Merge branch 'unstable'

parents e4f6b8c3 cf1eefa4
<!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>ZrangebyscoreCommand: Contents</b><br>&nbsp;&nbsp;<a href="#ZRANGEBYSCORE _key_ _min_ _max_ `[`LIMIT _offset_ _count_`]` (Redis &gt;">ZRANGEBYSCORE _key_ _min_ _max_ `[`LIMIT _offset_ _count_`]` (Redis &gt;</a><br>&nbsp;&nbsp;<a href="#ZRANGEBYSCORE _key_ _min_ _max_ `[`LIMIT _offset_ _count_`]` `[`WITHSCORES`]` (Redis &gt;">ZRANGEBYSCORE _key_ _min_ _max_ `[`LIMIT _offset_ _count_`]` `[`WITHSCORES`]` (Redis &gt;</a><br>&nbsp;&nbsp;<a href="#ZCOUNT _key_ _min_ _max_">ZCOUNT _key_ _min_ _max_</a><br>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Exclusive intervals and infinity">Exclusive intervals and infinity</a><br>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Return value">Return value</a><br>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Examples">Examples</a>
</div>
<h1 class="wikiname">ZrangebyscoreCommand</h1>
<div class="summary">
</div>
<div class="narrow">
&iuml;&raquo;&iquest;#sidebar <a href="SortedSetCommandsSidebar.html">SortedSetCommandsSidebar</a><h1><a name="ZRANGEBYSCORE _key_ _min_ _max_ `[`LIMIT _offset_ _count_`]` (Redis &gt;">ZRANGEBYSCORE _key_ _min_ _max_ `[`LIMIT _offset_ _count_`]` (Redis &gt;</a></h1> 1.1) =
<h1><a name="ZRANGEBYSCORE _key_ _min_ _max_ `[`LIMIT _offset_ _count_`]` `[`WITHSCORES`]` (Redis &gt;">ZRANGEBYSCORE _key_ _min_ _max_ `[`LIMIT _offset_ _count_`]` `[`WITHSCORES`]` (Redis &gt;</a></h1> 1.3.4) =
<h1><a name="ZCOUNT _key_ _min_ _max_">ZCOUNT _key_ _min_ _max_</a></h1>
<i>Time complexity: O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of elements returned by the command, so if M is constant (for instance you always ask for the first ten elements with LIMIT) you can consider it O(log(N))</i><blockquote>Return the all the elements in the sorted set at key with a score between_min_ and <i>max</i> (including elements with score equal to min or max).</blockquote>
<blockquote>The elements having the same score are returned sorted lexicographically asASCII strings (this follows from a property of Redis sorted sets and does notinvolve further computation).</blockquote>
<blockquote>Using the optional LIMIT it's possible to get only a range of the matchingelements in an SQL-alike way. Note that if <i>offset</i> is large the commandsneeds to traverse the list for <i>offset</i> elements and this adds up to theO(M) figure.</blockquote>
<blockquote>The <b>ZCOUNT</b> command is similar to <b>ZRANGEBYSCORE</b> but instead of returningthe actual elements in the specified interval, it just returns the numberof matching elements.</blockquote><h2><a name="Exclusive intervals and infinity">Exclusive intervals and infinity</a></h2>
<i>min</i> and <i>max</i> can be -inf and +inf, so that you are not required to know what's the greatest or smallest element in order to take, for instance, elements &quot;up to a given value&quot;.<br/><br/>Also while the interval is for default closed (inclusive) it's possible to specify open intervals prefixing the score with a &quot;(&quot; character, so for instance:
<pre class="codeblock python" name="code">
ZRANGEBYSCORE zset (1.3 5
</pre>
Will return all the values with score <b>&gt; 1.3 and &lt;= 5</b>, while for instance:
<pre class="codeblock python python" name="code">
ZRANGEBYSCORE zset (5 (10
</pre>
Will return all the values with score <b>&gt; 5 and &lt; 10</b> (5 and 10 excluded).
<h2><a name="Return value">Return value</a></h2>ZRANGEBYSCORE returns a <a href="ReplyTypes.html">Multi bulk reply</a> specifically a list of elements in the specified score range.<br/><br/>ZCOUNT returns a <a href="ReplyTypes.html">Integer reply</a> specifically the number of elements matching the specified score range.
<h2><a name="Examples">Examples</a></h2>
<pre class="codeblock python python python" name="code">
redis&gt; zadd zset 1 foo
(integer) 1
redis&gt; zadd zset 2 bar
(integer) 1
redis&gt; zadd zset 3 biz
(integer) 1
redis&gt; zadd zset 4 foz
(integer) 1
redis&gt; zrangebyscore zset -inf +inf
1. &quot;foo&quot;
2. &quot;bar&quot;
3. &quot;biz&quot;
4. &quot;foz&quot;
redis&gt; zcount zset 1 2
(integer) 2
redis&gt; zrangebyscore zset 1 2
1. &quot;foo&quot;
2. &quot;bar&quot;
redis&gt; zrangebyscore zset (1 2
1. &quot;bar&quot;
redis&gt; zrangebyscore zset (1 (2
(empty list or set)
</pre>
</div>
</div>
</div>
</body>
</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>ZrankCommand: Contents</b><br>&nbsp;&nbsp;<a href="#ZRANK _key_ _member_ (Redis &gt;">ZRANK _key_ _member_ (Redis &gt;</a><br>&nbsp;&nbsp;<a href="#ZREVRANK _key_ _member_ (Redis &gt;">ZREVRANK _key_ _member_ (Redis &gt;</a><br>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Return value">Return value</a>
</div>
<h1 class="wikiname">ZrankCommand</h1>
<div class="summary">
</div>
<div class="narrow">
<h1><a name="ZRANK _key_ _member_ (Redis &gt;">ZRANK _key_ _member_ (Redis &gt;</a></h1> 1.3.4) =
<h1><a name="ZREVRANK _key_ _member_ (Redis &gt;">ZREVRANK _key_ _member_ (Redis &gt;</a></h1> 1.3.4) =
<i>Time complexity: O(log(N))</i><blockquote>ZRANK returns the rank of the member in the sorted set, with scores ordered from low to high. ZREVRANK returns the rank with scores ordered from high to low. When the given member does not exist in the sorted set, the special value 'nil' is returned. The returned rank (or index) of the member is 0-based for both commands.</blockquote>
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a> or a nil <a href="ReplyTypes.html">bulk reply</a>, specifically:<br/><br/><pre class="codeblock python" name="code">
the rank of the element as an integer reply if the element exists.
A nil bulk reply if there is no such element.
</pre>
</div>
</div>
</div>
</body>
</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>ZremCommand: Contents</b><br>&nbsp;&nbsp;<a href="#ZREM _key_ _member_ (Redis &gt;">ZREM _key_ _member_ (Redis &gt;</a><br>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Return value">Return value</a>
</div>
<h1 class="wikiname">ZremCommand</h1>
<div class="summary">
</div>
<div class="narrow">
&iuml;&raquo;&iquest;#sidebar <a href="SortedSetCommandsSidebar.html">SortedSetCommandsSidebar</a><h1><a name="ZREM _key_ _member_ (Redis &gt;">ZREM _key_ _member_ (Redis &gt;</a></h1> 1.1) =
<i>Time complexity O(log(N)) with N being the number of elements in the sorted set</i><blockquote>Remove the specified <i>member</i> from the sorted set value stored at <i>key</i>. If_member_ was not a member of the set no operation is performed. If <i>key</i>does not not hold a set value an error is returned.</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 new element was removed
0 if the new element was not a member of the set
</pre>
</div>
</div>
</div>
</body>
</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>ZremrangebyrankCommand: Contents</b><br>&nbsp;&nbsp;<a href="#ZREMRANGEBYRANK _key_ _start_ _end_ (Redis &gt;">ZREMRANGEBYRANK _key_ _start_ _end_ (Redis &gt;</a><br>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Return value">Return value</a>
</div>
<h1 class="wikiname">ZremrangebyrankCommand</h1>
<div class="summary">
</div>
<div class="narrow">
<h1><a name="ZREMRANGEBYRANK _key_ _start_ _end_ (Redis &gt;">ZREMRANGEBYRANK _key_ _start_ _end_ (Redis &gt;</a></h1> 1.3.4) =
<i>Time complexity: O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of elements removed by the operation</i><blockquote>Remove all elements in the sorted set at <i>key</i> with rank between <i>start</i> and <i>end</i>. Start and end are 0-based with rank 0 being the element with the lowest score. Both start and end can be negative numbers, where they indicate offsets starting at the element with the highest rank. For example: -1 is the element with the highest score, -2 the element with the second highest score and so forth.</blockquote>
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>, specifically the number of elements removed.
</div>
</div>
</div>
</body>
</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>ZremrangebyscoreCommand: Contents</b><br>&nbsp;&nbsp;<a href="#ZREMRANGEBYSCORE _key_ _min_ _max_ (Redis &gt;">ZREMRANGEBYSCORE _key_ _min_ _max_ (Redis &gt;</a><br>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Return value">Return value</a>
</div>
<h1 class="wikiname">ZremrangebyscoreCommand</h1>
<div class="summary">
</div>
<div class="narrow">
&iuml;&raquo;&iquest;#sidebar <a href="SortedSetCommandsSidebar.html">SortedSetCommandsSidebar</a><h1><a name="ZREMRANGEBYSCORE _key_ _min_ _max_ (Redis &gt;">ZREMRANGEBYSCORE _key_ _min_ _max_ (Redis &gt;</a></h1> 1.1) =
<i>Time complexity: O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of elements removed by the operation</i><blockquote>Remove all the elements in the sorted set at key with a score between_min_ and <i>max</i> (including elements with score equal to min or max).</blockquote>
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>, specifically the number of elements removed.
</div>
</div>
</div>
</body>
</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>ZscoreCommand: Contents</b><br>&nbsp;&nbsp;<a href="#ZSCORE _key_ _element_ (Redis &gt;">ZSCORE _key_ _element_ (Redis &gt;</a><br>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Return value">Return value</a>
</div>
<h1 class="wikiname">ZscoreCommand</h1>
<div class="summary">
</div>
<div class="narrow">
&iuml;&raquo;&iquest;#sidebar <a href="SortedSetCommandsSidebar.html">SortedSetCommandsSidebar</a><h1><a name="ZSCORE _key_ _element_ (Redis &gt;">ZSCORE _key_ _element_ (Redis &gt;</a></h1> 1.1) =
<i>Time complexity O(1)</i><blockquote>Return the score of the specified element of the sorted set at key.If the specified element does not exist in the sorted set, or the keydoes not exist at all, a special 'nil' value is returned.</blockquote>
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Bulk reply</a><pre class="codeblock python" name="code">
the score (a double precision floating point number) represented as string.
</pre>
</div>
</div>
</div>
</body>
</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>ZunionCommand: Contents</b><br>&nbsp;&nbsp;<a href="#ZUNIONSTORE _dstkey_ _N_ _k1_ ... _kN_ `[`WEIGHTS _w1_ ... _wN_`]` `[`AGGREGATE SUM|MIN|MAX`]` (Redis &gt;">ZUNIONSTORE _dstkey_ _N_ _k1_ ... _kN_ `[`WEIGHTS _w1_ ... _wN_`]` `[`AGGREGATE SUM|MIN|MAX`]` (Redis &gt;</a><br>&nbsp;&nbsp;<a href="#ZINTERSTORE _dstkey_ _N_ _k1_ ... _kN_ `[`WEIGHTS _w1_ ... _wN_`]` `[`AGGREGATE SUM|MIN|MAX`]` (Redis &gt;">ZINTERSTORE _dstkey_ _N_ _k1_ ... _kN_ `[`WEIGHTS _w1_ ... _wN_`]` `[`AGGREGATE SUM|MIN|MAX`]` (Redis &gt;</a><br>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Return value">Return value</a>
</div>
<h1 class="wikiname">ZunionCommand</h1>
<div class="summary">
</div>
<div class="narrow">
<h1><a name="ZUNIONSTORE _dstkey_ _N_ _k1_ ... _kN_ `[`WEIGHTS _w1_ ... _wN_`]` `[`AGGREGATE SUM|MIN|MAX`]` (Redis &gt;">ZUNIONSTORE _dstkey_ _N_ _k1_ ... _kN_ `[`WEIGHTS _w1_ ... _wN_`]` `[`AGGREGATE SUM|MIN|MAX`]` (Redis &gt;</a></h1> 1.3.12) =
<h1><a name="ZINTERSTORE _dstkey_ _N_ _k1_ ... _kN_ `[`WEIGHTS _w1_ ... _wN_`]` `[`AGGREGATE SUM|MIN|MAX`]` (Redis &gt;">ZINTERSTORE _dstkey_ _N_ _k1_ ... _kN_ `[`WEIGHTS _w1_ ... _wN_`]` `[`AGGREGATE SUM|MIN|MAX`]` (Redis &gt;</a></h1> 1.3.12) =<br/><br/><i>Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set</i><blockquote>Creates a union or intersection of <i>N</i> sorted sets given by keys <i>k1</i> through <i>kN</i>, and stores it at <i>dstkey</i>. It is mandatory to provide the number of input keys <i>N</i>, before passing the input keys and the other (optional) arguments.</blockquote>
<blockquote>As the terms imply, the ZINTERSTORE command requires an element to be present in each of the given inputs to be inserted in the result. The ZUNIONSTORE command inserts all elements across all inputs.</blockquote>
<blockquote>Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means that the score of each element in the sorted set is first multiplied by this weight before being passed to the aggregation. When this option is not given, all weights default to 1.</blockquote>
<blockquote>With the AGGREGATE option, it's possible to specify how the results of the union or intersection are aggregated. This option defaults to SUM, where the score of an element is summed across the inputs where it exists. When this option is set to be either MIN or MAX, the resulting set will contain the minimum or maximum score of an element across the inputs where it exists.</blockquote>
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>, specifically the number of elements in the sorted set at <i>dstkey</i>.
</div>
</div>
</div>
</body>
</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>ZunionstoreCommand: Contents</b><br>&nbsp;&nbsp;<a href="#ZUNIONSTORE _dstkey_ _N_ _k1_ ... _kN_ `[`WEIGHTS _w1_ ... _wN_`]` `[`AGGREGATE SUM|MIN|MAX`]` (Redis &gt;">ZUNIONSTORE _dstkey_ _N_ _k1_ ... _kN_ `[`WEIGHTS _w1_ ... _wN_`]` `[`AGGREGATE SUM|MIN|MAX`]` (Redis &gt;</a><br>&nbsp;&nbsp;<a href="#ZINTERSTORE _dstkey_ _N_ _k1_ ... _kN_ `[`WEIGHTS _w1_ ... _wN_`]` `[`AGGREGATE SUM|MIN|MAX`]` (Redis &gt;">ZINTERSTORE _dstkey_ _N_ _k1_ ... _kN_ `[`WEIGHTS _w1_ ... _wN_`]` `[`AGGREGATE SUM|MIN|MAX`]` (Redis &gt;</a><br>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Return value">Return value</a>
</div>
<h1 class="wikiname">ZunionstoreCommand</h1>
<div class="summary">
</div>
<div class="narrow">
<h1><a name="ZUNIONSTORE _dstkey_ _N_ _k1_ ... _kN_ `[`WEIGHTS _w1_ ... _wN_`]` `[`AGGREGATE SUM|MIN|MAX`]` (Redis &gt;">ZUNIONSTORE _dstkey_ _N_ _k1_ ... _kN_ `[`WEIGHTS _w1_ ... _wN_`]` `[`AGGREGATE SUM|MIN|MAX`]` (Redis &gt;</a></h1> 1.3.12) =
<h1><a name="ZINTERSTORE _dstkey_ _N_ _k1_ ... _kN_ `[`WEIGHTS _w1_ ... _wN_`]` `[`AGGREGATE SUM|MIN|MAX`]` (Redis &gt;">ZINTERSTORE _dstkey_ _N_ _k1_ ... _kN_ `[`WEIGHTS _w1_ ... _wN_`]` `[`AGGREGATE SUM|MIN|MAX`]` (Redis &gt;</a></h1> 1.3.12) =
<i>Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set</i><blockquote>Creates a union or intersection of <i>N</i> sorted sets given by keys <i>k1</i> through <i>kN</i>, and stores it at <i>dstkey</i>. It is mandatory to provide the number of input keys <i>N</i>, before passing the input keys and the other (optional) arguments.</blockquote>
<blockquote>As the terms imply, the ZINTERSTORE command requires an element to be present in each of the given inputs to be inserted in the result. The ZUNIONSTORE command inserts all elements across all inputs.</blockquote>
<blockquote>Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means that the score of each element in the sorted set is first multiplied by this weight before being passed to the aggregation. When this option is not given, all weights default to 1.</blockquote>
<blockquote>With the AGGREGATE option, it's possible to specify how the results of the union or intersection are aggregated. This option defaults to SUM, where the score of an element is summed across the inputs where it exists. When this option is set to be either MIN or MAX, the resulting set will contain the minimum or maximum score of an element across the inputs where it exists.</blockquote>
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>, specifically the number of elements in the sorted set at <i>dstkey</i>.
</div>
</div>
</div>
</body>
</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>index: Contents</b><br>&nbsp;&nbsp;<a href="#Getting started">Getting started</a><br>&nbsp;&nbsp;<a href="#Full programming examples">Full programming examples</a><br>&nbsp;&nbsp;<a href="#FAQs and benchmarks">FAQs and benchmarks</a><br>&nbsp;&nbsp;<a href="#HOWTOs about selected features">HOWTOs about selected features</a><br>&nbsp;&nbsp;<a href="#Hacking">Hacking</a><br>&nbsp;&nbsp;<a href="#Videos">Videos</a><br>&nbsp;&nbsp;<a href="#Recipes and books">Recipes and books</a>
</div>
<h1 class="wikiname">index</h1>
<div class="summary">
</div>
<div class="narrow">
&iuml;&raquo;&iquest;= Redis Documentation =<br/><br/><a href="http://pyha.ru/wiki/index.php?title=Redis:index" target="_blank">Russian Translation</a>Hello! The followings are pointers to different parts of the Redis Documentation.<h1><a name="Getting started">Getting started</a></h1><ul><li> New! You can now <a href="http://try.redis-db.com" target="_blank">Try Redis directly in your browser!</a>.</li><li> <a href="README.html">The README</a> is a good starting point to know more about the project.</li><li> <a href="QuickStart.html">This short Quick Start</a> provides a five minutes step-by-step istructions on how to download, compile, run and test the basic workings of a Redis server.</li><li> <a href="CommandReference.html">The command reference</a> is a description of all the Redis commands with links to command specific pages. You can also download the <a href="http://go2.wordpress.com/?id=725X1342&site=masonoise.wordpress.com&url=http%3A%2F%2Fmasonoise.files.wordpress.com%2F2010%2F03%2Fredis-cheatsheet-v1.pdf" target="_blank">Redis Commands Cheat-Sheet</a> provided by Mason Jones (btw some command may be missing, the primary source is the wiki).</li><li> <a href="IntroductionToRedisDataTypes.html">A Fifteen Minutes Introduction to the Redis Data Types</a> explains how Redis data types work and the basic patterns of working with Redis.</li><li> <a href="http://simonwillison.net/static/2010/redis-tutorial/" target="_blank">the Simon Willison Redis Tutorial</a> is a <b>must read</b>, very good documentation where you will find a lot of real world ideas and use cases.</li><li> <a href="http://blog.mjrusso.com/2010/10/17/redis-from-the-ground-up.html" target="_blank">Redis from the ground up</a> is an impressive article to know more about Redis if you are a newcomer.</li><li> <a href="http://playnice.ly/blog/2010/10/19/getting-started-redis-python/" target="_blank">Getting started with Redis and Python</a></li></ul>
<h1><a name="Full programming examples">Full programming examples</a></h1><ul><li> <a href="TwitterAlikeExample.html">This is a tutorial about creating a Twitter clone using *only* Redis as database, no relational DB at all is used</a>, it is a good start to understand the key-value database paradigm.</li></ul>
<h1><a name="FAQs and benchmarks">FAQs and benchmarks</a></h1><ul><li> <a href="Benchmarks.html">The benchmark page</a> is about the speed performances of Redis.</li><li> <a href="FAQ.html">Our FAQ</a> contains of course some answers to common questions about Redis. Not very up-to-date.</li></ul>
<h1><a name="HOWTOs about selected features">HOWTOs about selected features</a></h1><ul><li> <a href="ReplicationHowto.html">The Redis Replication HOWTO</a> is what you need to read in order to understand how Redis master <code name="code" class="python">&lt;-&gt;</code> slave replication works.</li><li> <a href="AppendOnlyFileHowto.html">The Append Only File HOWTO</a> explains how the alternative Redis durability mode works. AOF is an alternative to snapshotting on disk from time to time (the default).</li><li> <a href="VirtualMemoryUserGuide.html">Virtual Memory User Guide</a>. A simple to understand guide about using and configuring the Redis Virtual Memory.</li><li> <a href="RedisPipelining.html">Redis Pipelining Guide</a>.</li><li> Memory optimization: Full of keys, a <a href="http://antirez.com/post/redis-weekly-update-7.html" target="_blank">blog post at antirez.com</a> showing how to use Redis 2.0 and hashes to create a setup where you can store 5 times more data in your Redis instance.</li><li> <a href="http://antirez.com/post/autocomplete-with-redis.html" target="_blank">Implementing auto complete with Redis</a>.</li></ul>
<h1><a name="Hacking">Hacking</a></h1>
<ul><li> <a href="ProtocolSpecification.html">The Protocol Specification</a> is all you need in order to implement a Redis client library for a missing language. PHP, Python, Ruby and Erlang are already supported.</li><li> Look at <a href="RedisInternals.html">Redis Internals</a> if you are interested in the implementation details of the Redis server.</li></ul>
<h1><a name="Videos">Videos</a></h1><ul><li> <a href="http://confreaks.net/videos/62-mwrc2009-redis-key-value-nirvana" target="_blank">Ezra Zygmuntowicz at the Mountain West Ruby Conference 2009</a> to know the most important Redis ideas in few minutes.</li><li> <a href="http://www.ustream.tv/recorded/7855635" target="_blank">Salvatore Sanfilippo and Pieter Noordhuis at the SF Redis Meetup</a></li></ul>
<h1><a name="Recipes and books">Recipes and books</a></h1><ul><li> <a href="http://www.rediscookbook.org/" target="_blank">The Redis Cookbook</a> is a collaborative effort to provide some good recipe.</li><li> There is an ongoing effort to write a Redis book for O'Reilly</li></ul>
</div>
</div>
</div>
</body>
</html>
body {
font-family: 'Verdana';
width:800px;
}
a {
text-decoration: none;
}
h1, h2, h3, h4, h5 {
color: #333;
}
h1 {
font-size: 20px;
}
.codeblock {
color: darkgreen;
padding:5px;
}
h1.wikiname {
color: #f55000;
}
...@@ -565,16 +565,18 @@ werr: ...@@ -565,16 +565,18 @@ werr:
*/ */
int rewriteAppendOnlyFileBackground(void) { int rewriteAppendOnlyFileBackground(void) {
pid_t childpid; pid_t childpid;
long long start;
if (server.bgrewritechildpid != -1) return REDIS_ERR; if (server.bgrewritechildpid != -1) return REDIS_ERR;
if (server.ds_enabled != 0) { if (server.ds_enabled != 0) {
redisLog(REDIS_WARNING,"BGREWRITEAOF called with diskstore enabled: AOF is not supported when diskstore is enabled. Operation not performed."); redisLog(REDIS_WARNING,"BGREWRITEAOF called with diskstore enabled: AOF is not supported when diskstore is enabled. Operation not performed.");
return REDIS_ERR; return REDIS_ERR;
} }
start = ustime();
if ((childpid = fork()) == 0) { if ((childpid = fork()) == 0) {
/* Child */
char tmpfile[256]; char tmpfile[256];
/* Child */
if (server.ipfd > 0) close(server.ipfd); if (server.ipfd > 0) close(server.ipfd);
if (server.sofd > 0) close(server.sofd); if (server.sofd > 0) close(server.sofd);
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid()); snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid());
...@@ -585,6 +587,7 @@ int rewriteAppendOnlyFileBackground(void) { ...@@ -585,6 +587,7 @@ int rewriteAppendOnlyFileBackground(void) {
} }
} else { } else {
/* Parent */ /* Parent */
server.stat_fork_time = ustime()-start;
if (childpid == -1) { if (childpid == -1) {
redisLog(REDIS_WARNING, redisLog(REDIS_WARNING,
"Can't rewrite append only file in background: fork: %s", "Can't rewrite append only file in background: fork: %s",
......
...@@ -180,7 +180,7 @@ void decrRefCount(void *obj) { ...@@ -180,7 +180,7 @@ void decrRefCount(void *obj) {
robj *o = obj; robj *o = obj;
if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0"); if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
if (--(o->refcount) == 0) { if (o->refcount == 1) {
switch(o->type) { switch(o->type) {
case REDIS_STRING: freeStringObject(o); break; case REDIS_STRING: freeStringObject(o); break;
case REDIS_LIST: freeListObject(o); break; case REDIS_LIST: freeListObject(o); break;
...@@ -189,8 +189,9 @@ void decrRefCount(void *obj) { ...@@ -189,8 +189,9 @@ void decrRefCount(void *obj) {
case REDIS_HASH: freeHashObject(o); break; case REDIS_HASH: freeHashObject(o); break;
default: redisPanic("Unknown object type"); break; default: redisPanic("Unknown object type"); break;
} }
o->ptr = NULL; /* defensive programming. We'll see NULL in traces. */
zfree(o); zfree(o);
} else {
o->refcount--;
} }
} }
......
...@@ -482,6 +482,7 @@ werr: ...@@ -482,6 +482,7 @@ werr:
int rdbSaveBackground(char *filename) { int rdbSaveBackground(char *filename) {
pid_t childpid; pid_t childpid;
long long start;
if (server.bgsavechildpid != -1 || if (server.bgsavechildpid != -1 ||
server.bgsavethread != (pthread_t) -1) return REDIS_ERR; server.bgsavethread != (pthread_t) -1) return REDIS_ERR;
...@@ -493,6 +494,7 @@ int rdbSaveBackground(char *filename) { ...@@ -493,6 +494,7 @@ int rdbSaveBackground(char *filename) {
return dsRdbSaveBackground(filename); return dsRdbSaveBackground(filename);
} }
start = ustime();
if ((childpid = fork()) == 0) { if ((childpid = fork()) == 0) {
int retval; int retval;
...@@ -503,6 +505,7 @@ int rdbSaveBackground(char *filename) { ...@@ -503,6 +505,7 @@ int rdbSaveBackground(char *filename) {
_exit((retval == REDIS_OK) ? 0 : 1); _exit((retval == REDIS_OK) ? 0 : 1);
} else { } else {
/* Parent */ /* Parent */
server.stat_fork_time = ustime()-start;
if (childpid == -1) { if (childpid == -1) {
redisLog(REDIS_WARNING,"Can't save in background: fork: %s", redisLog(REDIS_WARNING,"Can't save in background: fork: %s",
strerror(errno)); strerror(errno));
......
...@@ -55,6 +55,7 @@ static struct config { ...@@ -55,6 +55,7 @@ static struct config {
int hostport; int hostport;
char *hostsocket; char *hostsocket;
long repeat; long repeat;
long interval;
int dbnum; int dbnum;
int interactive; int interactive;
int shutdown; int shutdown;
...@@ -87,9 +88,11 @@ static long long mstime(void) { ...@@ -87,9 +88,11 @@ static long long mstime(void) {
static void cliRefreshPrompt(void) { static void cliRefreshPrompt(void) {
if (config.dbnum == 0) if (config.dbnum == 0)
snprintf(config.prompt,sizeof(config.prompt),"redis> "); snprintf(config.prompt,sizeof(config.prompt),"redis %s:%d> ",
config.hostip, config.hostport);
else else
snprintf(config.prompt,sizeof(config.prompt),"redis:%d> ",config.dbnum); snprintf(config.prompt,sizeof(config.prompt),"redis %s:%d[%d]> ",
config.hostip, config.hostport, config.dbnum);
} }
/*------------------------------------------------------------------------------ /*------------------------------------------------------------------------------
...@@ -314,10 +317,9 @@ static int cliConnect(int force) { ...@@ -314,10 +317,9 @@ static int cliConnect(int force) {
return REDIS_OK; return REDIS_OK;
} }
static void cliPrintContextErrorAndExit() { static void cliPrintContextError() {
if (context == NULL) return; if (context == NULL) return;
fprintf(stderr,"Error: %s\n",context->errstr); fprintf(stderr,"Error: %s\n",context->errstr);
exit(1);
} }
static sds cliFormatReplyTTY(redisReply *r, char *prefix) { static sds cliFormatReplyTTY(redisReply *r, char *prefix) {
...@@ -436,7 +438,8 @@ static int cliReadReply(int output_raw_strings) { ...@@ -436,7 +438,8 @@ static int cliReadReply(int output_raw_strings) {
if (context->err == REDIS_ERR_EOF) if (context->err == REDIS_ERR_EOF)
return REDIS_ERR; return REDIS_ERR;
} }
cliPrintContextErrorAndExit(); cliPrintContextError();
exit(1);
return REDIS_ERR; /* avoid compiler warning */ return REDIS_ERR; /* avoid compiler warning */
} }
...@@ -462,10 +465,7 @@ static int cliSendCommand(int argc, char **argv, int repeat) { ...@@ -462,10 +465,7 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
size_t *argvlen; size_t *argvlen;
int j, output_raw; int j, output_raw;
if (context == NULL) { if (context == NULL) return REDIS_ERR;
printf("Not connected, please use: connect <host> <port>\n");
return REDIS_OK;
}
output_raw = 0; output_raw = 0;
if (!strcasecmp(command,"info") || if (!strcasecmp(command,"info") ||
...@@ -518,6 +518,8 @@ static int cliSendCommand(int argc, char **argv, int repeat) { ...@@ -518,6 +518,8 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
cliRefreshPrompt(); cliRefreshPrompt();
} }
} }
if (config.interval) usleep(config.interval);
fflush(stdout); /* Make it grep friendly */
} }
free(argvlen); free(argvlen);
...@@ -553,6 +555,10 @@ static int parseOptions(int argc, char **argv) { ...@@ -553,6 +555,10 @@ static int parseOptions(int argc, char **argv) {
} else if (!strcmp(argv[i],"-r") && !lastarg) { } else if (!strcmp(argv[i],"-r") && !lastarg) {
config.repeat = strtoll(argv[i+1],NULL,10); config.repeat = strtoll(argv[i+1],NULL,10);
i++; i++;
} else if (!strcmp(argv[i],"-i") && !lastarg) {
double seconds = atof(argv[i+1]);
config.interval = seconds*1000000;
i++;
} else if (!strcmp(argv[i],"-n") && !lastarg) { } else if (!strcmp(argv[i],"-n") && !lastarg) {
config.dbnum = atoi(argv[i+1]); config.dbnum = atoi(argv[i+1]);
i++; i++;
...@@ -605,6 +611,8 @@ static void usage() { ...@@ -605,6 +611,8 @@ static void usage() {
" -s <socket> Server socket (overrides hostname and port)\n" " -s <socket> Server socket (overrides hostname and port)\n"
" -a <password> Password to use when connecting to the server\n" " -a <password> Password to use when connecting to the server\n"
" -r <repeat> Execute specified command N times\n" " -r <repeat> Execute specified command N times\n"
" -i <interval> When -r is used, waits <interval> seconds per command.\n"
" It is possible to specify sub-second times like -i 0.1.\n"
" -n <db> Database number\n" " -n <db> Database number\n"
" -x Read last argument from STDIN\n" " -x Read last argument from STDIN\n"
" -d <delimiter> Multi-bulk delimiter in for raw formatting (default: \\n)\n" " -d <delimiter> Multi-bulk delimiter in for raw formatting (default: \\n)\n"
...@@ -616,6 +624,7 @@ static void usage() { ...@@ -616,6 +624,7 @@ static void usage() {
" cat /etc/passwd | redis-cli -x set mypasswd\n" " cat /etc/passwd | redis-cli -x set mypasswd\n"
" redis-cli get mypasswd\n" " redis-cli get mypasswd\n"
" redis-cli -r 100 lpush mylist x\n" " redis-cli -r 100 lpush mylist x\n"
" redis-cli -r 100 -i 1 info | grep used_memory_human:\n"
"\n" "\n"
"When no command is given, redis-cli starts in interactive mode.\n" "When no command is given, redis-cli starts in interactive mode.\n"
"Type \"help\" in interactive mode for information on available commands.\n" "Type \"help\" in interactive mode for information on available commands.\n"
...@@ -681,14 +690,25 @@ static void repl() { ...@@ -681,14 +690,25 @@ static void repl() {
linenoiseClearScreen(); linenoiseClearScreen();
} else { } else {
long long start_time = mstime(), elapsed; long long start_time = mstime(), elapsed;
int repeat, skipargs = 0;
repeat = atoi(argv[0]);
if (repeat) {
skipargs = 1;
} else {
repeat = 1;
}
if (cliSendCommand(argc,argv,1) != REDIS_OK) { if (cliSendCommand(argc-skipargs,argv+skipargs,repeat)
!= REDIS_OK)
{
cliConnect(1); cliConnect(1);
/* If we still cannot send the command, /* If we still cannot send the command print error.
* print error and abort. */ * We'll try to reconnect the next time. */
if (cliSendCommand(argc,argv,1) != REDIS_OK) if (cliSendCommand(argc-skipargs,argv+skipargs,repeat)
cliPrintContextErrorAndExit(); != REDIS_OK)
cliPrintContextError();
} }
elapsed = mstime()-start_time; elapsed = mstime()-start_time;
if (elapsed >= 500) { if (elapsed >= 500) {
...@@ -726,6 +746,7 @@ int main(int argc, char **argv) { ...@@ -726,6 +746,7 @@ int main(int argc, char **argv) {
config.hostport = 6379; config.hostport = 6379;
config.hostsocket = NULL; config.hostsocket = NULL;
config.repeat = 1; config.repeat = 1;
config.interval = 0;
config.dbnum = 0; config.dbnum = 0;
config.interactive = 0; config.interactive = 0;
config.shutdown = 0; config.shutdown = 0;
...@@ -741,11 +762,15 @@ int main(int argc, char **argv) { ...@@ -741,11 +762,15 @@ int main(int argc, char **argv) {
argc -= firstarg; argc -= firstarg;
argv += firstarg; argv += firstarg;
/* Try to connect */
if (cliConnect(0) != REDIS_OK) exit(1);
/* Start interactive mode when no command is provided */ /* Start interactive mode when no command is provided */
if (argc == 0) repl(); if (argc == 0) {
/* Note that in repl mode we don't abort on connection error.
* A new attempt will be performed for every command send. */
cliConnect(0);
repl();
}
/* Otherwise, we have some arguments to execute */ /* Otherwise, we have some arguments to execute */
if (cliConnect(0) != REDIS_OK) exit(1);
return noninteractive(argc,convertToSds(argc,argv)); return noninteractive(argc,convertToSds(argc,argv));
} }
...@@ -116,9 +116,9 @@ struct redisCommand redisCommandTable[] = { ...@@ -116,9 +116,9 @@ struct redisCommand redisCommandTable[] = {
{"sdiff",sdiffCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-1,1,0,0}, {"sdiff",sdiffCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-1,1,0,0},
{"sdiffstore",sdiffstoreCommand,-3,REDIS_CMD_DENYOOM,NULL,2,-1,1,0,0}, {"sdiffstore",sdiffstoreCommand,-3,REDIS_CMD_DENYOOM,NULL,2,-1,1,0,0},
{"smembers",sinterCommand,2,0,NULL,1,1,1,0,0}, {"smembers",sinterCommand,2,0,NULL,1,1,1,0,0},
{"zadd",zaddCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0}, {"zadd",zaddCommand,-4,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
{"zincrby",zincrbyCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0}, {"zincrby",zincrbyCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
{"zrem",zremCommand,3,0,NULL,1,1,1,0,0}, {"zrem",zremCommand,-3,0,NULL,1,1,1,0,0},
{"zremrangebyscore",zremrangebyscoreCommand,4,0,NULL,1,1,1,0,0}, {"zremrangebyscore",zremrangebyscoreCommand,4,0,NULL,1,1,1,0,0},
{"zremrangebyrank",zremrangebyrankCommand,4,0,NULL,1,1,1,0,0}, {"zremrangebyrank",zremrangebyrankCommand,4,0,NULL,1,1,1,0,0},
{"zunionstore",zunionstoreCommand,-4,REDIS_CMD_DENYOOM,zunionInterGetKeys,0,0,0,0,0}, {"zunionstore",zunionstoreCommand,-4,REDIS_CMD_DENYOOM,zunionInterGetKeys,0,0,0,0,0},
...@@ -871,6 +871,7 @@ void initServerConfig() { ...@@ -871,6 +871,7 @@ void initServerConfig() {
server.masterport = 6379; server.masterport = 6379;
server.master = NULL; server.master = NULL;
server.replstate = REDIS_REPL_NONE; server.replstate = REDIS_REPL_NONE;
server.repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT;
server.repl_serve_stale_data = 1; server.repl_serve_stale_data = 1;
/* Double constants initialization */ /* Double constants initialization */
...@@ -963,6 +964,7 @@ void initServer() { ...@@ -963,6 +964,7 @@ void initServer() {
server.stat_keyspace_misses = 0; server.stat_keyspace_misses = 0;
server.stat_keyspace_hits = 0; server.stat_keyspace_hits = 0;
server.stat_peak_memory = 0; server.stat_peak_memory = 0;
server.stat_fork_time = 0;
server.unixtime = time(NULL); server.unixtime = time(NULL);
aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL); aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL);
if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE, if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE,
...@@ -1438,7 +1440,8 @@ sds genRedisInfoString(char *section) { ...@@ -1438,7 +1440,8 @@ sds genRedisInfoString(char *section) {
"keyspace_hits:%lld\r\n" "keyspace_hits:%lld\r\n"
"keyspace_misses:%lld\r\n" "keyspace_misses:%lld\r\n"
"pubsub_channels:%ld\r\n" "pubsub_channels:%ld\r\n"
"pubsub_patterns:%u\r\n", "pubsub_patterns:%u\r\n"
"latest_fork_usec:%lld\r\n",
server.stat_numconnections, server.stat_numconnections,
server.stat_numcommands, server.stat_numcommands,
server.stat_expiredkeys, server.stat_expiredkeys,
...@@ -1446,7 +1449,8 @@ sds genRedisInfoString(char *section) { ...@@ -1446,7 +1449,8 @@ sds genRedisInfoString(char *section) {
server.stat_keyspace_hits, server.stat_keyspace_hits,
server.stat_keyspace_misses, server.stat_keyspace_misses,
dictSize(server.pubsub_channels), dictSize(server.pubsub_channels),
listLength(server.pubsub_patterns)); listLength(server.pubsub_patterns),
server.stat_fork_time);
} }
/* Replication */ /* Replication */
......
...@@ -41,7 +41,6 @@ ...@@ -41,7 +41,6 @@
#define REDIS_MAXIDLETIME (60*5) /* default client timeout */ #define REDIS_MAXIDLETIME (60*5) /* default client timeout */
#define REDIS_IOBUF_LEN 1024 #define REDIS_IOBUF_LEN 1024
#define REDIS_LOADBUF_LEN 1024 #define REDIS_LOADBUF_LEN 1024
#define REDIS_STATIC_ARGS 8
#define REDIS_DEFAULT_DBNUM 16 #define REDIS_DEFAULT_DBNUM 16
#define REDIS_CONFIGLINE_MAX 1024 #define REDIS_CONFIGLINE_MAX 1024
#define REDIS_MAX_SYNC_TIME 60 /* Slave can't take more to sync */ #define REDIS_MAX_SYNC_TIME 60 /* Slave can't take more to sync */
...@@ -153,10 +152,14 @@ ...@@ -153,10 +152,14 @@
#define REDIS_REQ_MULTIBULK 2 #define REDIS_REQ_MULTIBULK 2
/* Slave replication state - slave side */ /* Slave replication state - slave side */
#define REDIS_REPL_NONE 0 /* No active replication */ #define REDIS_REPL_NONE 0 /* No active replication */
#define REDIS_REPL_CONNECT 1 /* Must connect to master */ #define REDIS_REPL_CONNECT 1 /* Must connect to master */
#define REDIS_REPL_TRANSFER 2 /* Receiving .rdb from master */ #define REDIS_REPL_CONNECTING 2 /* Connecting to master */
#define REDIS_REPL_CONNECTED 3 /* Connected to master */ #define REDIS_REPL_TRANSFER 3 /* Receiving .rdb from master */
#define REDIS_REPL_CONNECTED 4 /* Connected to master */
/* Synchronous read timeout - slave side */
#define REDIS_REPL_SYNCIO_TIMEOUT 5
/* Slave replication state - from the point of view of master /* Slave replication state - from the point of view of master
* Note that in SEND_BULK and ONLINE state the slave receives new updates * Note that in SEND_BULK and ONLINE state the slave receives new updates
...@@ -543,6 +546,7 @@ struct redisServer { ...@@ -543,6 +546,7 @@ struct redisServer {
long long stat_keyspace_hits; /* number of successful lookups of keys */ long long stat_keyspace_hits; /* number of successful lookups of keys */
long long stat_keyspace_misses; /* number of failed lookups of keys */ long long stat_keyspace_misses; /* number of failed lookups of keys */
size_t stat_peak_memory; /* max used memory record */ size_t stat_peak_memory; /* max used memory record */
long long stat_fork_time; /* time needed to perform latets fork() */
/* Configuration */ /* Configuration */
int verbosity; int verbosity;
int maxidletime; int maxidletime;
...@@ -585,6 +589,7 @@ struct redisServer { ...@@ -585,6 +589,7 @@ struct redisServer {
char *masterhost; char *masterhost;
int masterport; int masterport;
redisClient *master; /* client that is master for this slave */ redisClient *master; /* client that is master for this slave */
int repl_syncio_timeout; /* timeout for synchronous I/O calls */
int replstate; /* replication status if the instance is a slave */ int replstate; /* replication status if the instance is a slave */
off_t repl_transfer_left; /* bytes left reading .rdb */ off_t repl_transfer_left; /* bytes left reading .rdb */
int repl_transfer_s; /* slave -> master SYNC socket */ int repl_transfer_s; /* slave -> master SYNC socket */
...@@ -888,7 +893,6 @@ int fwriteBulkCount(FILE *fp, char prefix, int count); ...@@ -888,7 +893,6 @@ int fwriteBulkCount(FILE *fp, char prefix, int count);
/* Replication */ /* Replication */
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc); void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc);
void replicationFeedMonitors(list *monitors, int dictid, robj **argv, int argc); void replicationFeedMonitors(list *monitors, int dictid, robj **argv, int argc);
int syncWithMaster(void);
void updateSlavesWaitingBgsave(int bgsaveerr); void updateSlavesWaitingBgsave(int bgsaveerr);
void replicationCron(void); void replicationCron(void);
......
...@@ -10,38 +10,8 @@ ...@@ -10,38 +10,8 @@
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) { void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
listNode *ln; listNode *ln;
listIter li; listIter li;
int outc = 0, j; int j;
robj **outv;
/* We need 1+(ARGS*3) objects since commands are using the new protocol
* and we one 1 object for the first "*<count>\r\n" multibulk count, then
* for every additional object we have "$<count>\r\n" + object + "\r\n". */
robj *static_outv[REDIS_STATIC_ARGS*3+1];
robj *lenobj;
if (argc <= REDIS_STATIC_ARGS) {
outv = static_outv;
} else {
outv = zmalloc(sizeof(robj*)*(argc*3+1));
}
lenobj = createObject(REDIS_STRING,
sdscatprintf(sdsempty(), "*%d\r\n", argc));
lenobj->refcount = 0;
outv[outc++] = lenobj;
for (j = 0; j < argc; j++) {
lenobj = createObject(REDIS_STRING,
sdscatprintf(sdsempty(),"$%lu\r\n",
(unsigned long) stringObjectLen(argv[j])));
lenobj->refcount = 0;
outv[outc++] = lenobj;
outv[outc++] = argv[j];
outv[outc++] = shared.crlf;
}
/* Increment all the refcounts at start and decrement at end in order to
* be sure to free objects if there is no slave in a replication state
* able to be feed with commands */
for (j = 0; j < outc; j++) incrRefCount(outv[j]);
listRewind(slaves,&li); listRewind(slaves,&li);
while((ln = listNext(&li))) { while((ln = listNext(&li))) {
redisClient *slave = ln->value; redisClient *slave = ln->value;
...@@ -49,7 +19,9 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) { ...@@ -49,7 +19,9 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
/* Don't feed slaves that are still waiting for BGSAVE to start */ /* Don't feed slaves that are still waiting for BGSAVE to start */
if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START) continue; if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START) continue;
/* Feed all the other slaves, MONITORs and so on */ /* Feed slaves that are waiting for the initial SYNC (so these commands
* are queued in the output buffer until the intial SYNC completes),
* or are already in sync with the master. */
if (slave->slaveseldb != dictid) { if (slave->slaveseldb != dictid) {
robj *selectcmd; robj *selectcmd;
...@@ -73,10 +45,9 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) { ...@@ -73,10 +45,9 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
addReply(slave,selectcmd); addReply(slave,selectcmd);
slave->slaveseldb = dictid; slave->slaveseldb = dictid;
} }
for (j = 0; j < outc; j++) addReply(slave,outv[j]); addReplyMultiBulkLen(slave,argc);
for (j = 0; j < argc; j++) addReplyBulk(slave,argv[j]);
} }
for (j = 0; j < outc; j++) decrRefCount(outv[j]);
if (outv != static_outv) zfree(outv);
} }
void replicationFeedMonitors(list *monitors, int dictid, robj **argv, int argc) { void replicationFeedMonitors(list *monitors, int dictid, robj **argv, int argc) {
...@@ -315,19 +286,18 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) { ...@@ -315,19 +286,18 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
/* If repl_transfer_left == -1 we still have to read the bulk length /* If repl_transfer_left == -1 we still have to read the bulk length
* from the master reply. */ * from the master reply. */
if (server.repl_transfer_left == -1) { if (server.repl_transfer_left == -1) {
if (syncReadLine(fd,buf,1024,3600) == -1) { if (syncReadLine(fd,buf,1024,server.repl_syncio_timeout) == -1) {
redisLog(REDIS_WARNING, redisLog(REDIS_WARNING,
"I/O error reading bulk count from MASTER: %s", "I/O error reading bulk count from MASTER: %s",
strerror(errno)); strerror(errno));
replicationAbortSyncTransfer(); goto error;
return;
} }
if (buf[0] == '-') { if (buf[0] == '-') {
redisLog(REDIS_WARNING, redisLog(REDIS_WARNING,
"MASTER aborted replication with an error: %s", "MASTER aborted replication with an error: %s",
buf+1); buf+1);
replicationAbortSyncTransfer(); goto error;
return;
} else if (buf[0] == '\0') { } else if (buf[0] == '\0') {
/* At this stage just a newline works as a PING in order to take /* At this stage just a newline works as a PING in order to take
* the connection live. So we refresh our last interaction * the connection live. So we refresh our last interaction
...@@ -336,8 +306,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) { ...@@ -336,8 +306,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
return; return;
} else if (buf[0] != '$') { } else if (buf[0] != '$') {
redisLog(REDIS_WARNING,"Bad protocol from MASTER, the first byte is not '$', are you sure the host and port are right?"); redisLog(REDIS_WARNING,"Bad protocol from MASTER, the first byte is not '$', are you sure the host and port are right?");
replicationAbortSyncTransfer(); goto error;
return;
} }
server.repl_transfer_left = strtol(buf+1,NULL,10); server.repl_transfer_left = strtol(buf+1,NULL,10);
redisLog(REDIS_NOTICE, redisLog(REDIS_NOTICE,
...@@ -359,8 +328,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) { ...@@ -359,8 +328,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
server.repl_transfer_lastio = time(NULL); server.repl_transfer_lastio = time(NULL);
if (write(server.repl_transfer_fd,buf,nread) != nread) { if (write(server.repl_transfer_fd,buf,nread) != nread) {
redisLog(REDIS_WARNING,"Write error or short write writing to the DB dump file needed for MASTER <-> SLAVE synchrnonization: %s", strerror(errno)); redisLog(REDIS_WARNING,"Write error or short write writing to the DB dump file needed for MASTER <-> SLAVE synchrnonization: %s", strerror(errno));
replicationAbortSyncTransfer(); goto error;
return;
} }
server.repl_transfer_left -= nread; server.repl_transfer_left -= nread;
/* Check if the transfer is now complete */ /* Check if the transfer is now complete */
...@@ -391,48 +359,54 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) { ...@@ -391,48 +359,54 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
server.replstate = REDIS_REPL_CONNECTED; server.replstate = REDIS_REPL_CONNECTED;
redisLog(REDIS_NOTICE, "MASTER <-> SLAVE sync: Finished with success"); redisLog(REDIS_NOTICE, "MASTER <-> SLAVE sync: Finished with success");
} }
return;
error:
replicationAbortSyncTransfer();
return;
} }
int syncWithMaster(void) { void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
char buf[1024], tmpfile[256], authcmd[1024]; char buf[1024], tmpfile[256];
int fd = anetTcpConnect(NULL,server.masterhost,server.masterport);
int dfd, maxtries = 5; int dfd, maxtries = 5;
REDIS_NOTUSED(el);
REDIS_NOTUSED(privdata);
REDIS_NOTUSED(mask);
if (fd == -1) { /* This event should only be triggered once since it is used to have a
redisLog(REDIS_WARNING,"Unable to connect to MASTER: %s", * non-blocking connect(2) to the master. It has been triggered when this
strerror(errno)); * function is called, so we can delete it. */
return REDIS_ERR; aeDeleteFileEvent(server.el,fd,AE_WRITABLE);
}
/* AUTH with the master if required. */ /* AUTH with the master if required. */
if(server.masterauth) { if(server.masterauth) {
snprintf(authcmd, 1024, "AUTH %s\r\n", server.masterauth); char authcmd[1024];
if (syncWrite(fd, authcmd, strlen(server.masterauth)+7, 5) == -1) { size_t authlen;
close(fd);
authlen = snprintf(authcmd,sizeof(authcmd),"AUTH %s\r\n",server.masterauth);
if (syncWrite(fd,authcmd,authlen,server.repl_syncio_timeout) == -1) {
redisLog(REDIS_WARNING,"Unable to AUTH to MASTER: %s", redisLog(REDIS_WARNING,"Unable to AUTH to MASTER: %s",
strerror(errno)); strerror(errno));
return REDIS_ERR; goto error;
} }
/* Read the AUTH result. */ /* Read the AUTH result. */
if (syncReadLine(fd,buf,1024,3600) == -1) { if (syncReadLine(fd,buf,1024,server.repl_syncio_timeout) == -1) {
close(fd);
redisLog(REDIS_WARNING,"I/O error reading auth result from MASTER: %s", redisLog(REDIS_WARNING,"I/O error reading auth result from MASTER: %s",
strerror(errno)); strerror(errno));
return REDIS_ERR; goto error;
} }
if (buf[0] != '+') { if (buf[0] != '+') {
close(fd);
redisLog(REDIS_WARNING,"Cannot AUTH to MASTER, is the masterauth password correct?"); redisLog(REDIS_WARNING,"Cannot AUTH to MASTER, is the masterauth password correct?");
return REDIS_ERR; goto error;
} }
} }
/* Issue the SYNC command */ /* Issue the SYNC command */
if (syncWrite(fd,"SYNC \r\n",7,5) == -1) { if (syncWrite(fd,"SYNC \r\n",7,server.repl_syncio_timeout) == -1) {
close(fd);
redisLog(REDIS_WARNING,"I/O error writing to MASTER: %s", redisLog(REDIS_WARNING,"I/O error writing to MASTER: %s",
strerror(errno)); strerror(errno));
return REDIS_ERR; goto error;
} }
/* Prepare a suitable temp file for bulk transfer */ /* Prepare a suitable temp file for bulk transfer */
...@@ -444,25 +418,51 @@ int syncWithMaster(void) { ...@@ -444,25 +418,51 @@ int syncWithMaster(void) {
sleep(1); sleep(1);
} }
if (dfd == -1) { if (dfd == -1) {
close(fd);
redisLog(REDIS_WARNING,"Opening the temp file needed for MASTER <-> SLAVE synchronization: %s",strerror(errno)); redisLog(REDIS_WARNING,"Opening the temp file needed for MASTER <-> SLAVE synchronization: %s",strerror(errno));
return REDIS_ERR; goto error;
} }
/* Setup the non blocking download of the bulk file. */ /* Setup the non blocking download of the bulk file. */
if (aeCreateFileEvent(server.el, fd, AE_READABLE, readSyncBulkPayload, NULL) if (aeCreateFileEvent(server.el,fd, AE_READABLE,readSyncBulkPayload,NULL)
== AE_ERR) == AE_ERR)
{ {
close(fd);
redisLog(REDIS_WARNING,"Can't create readable event for SYNC"); redisLog(REDIS_WARNING,"Can't create readable event for SYNC");
return REDIS_ERR; goto error;
} }
server.replstate = REDIS_REPL_TRANSFER; server.replstate = REDIS_REPL_TRANSFER;
server.repl_transfer_left = -1; server.repl_transfer_left = -1;
server.repl_transfer_s = fd;
server.repl_transfer_fd = dfd; server.repl_transfer_fd = dfd;
server.repl_transfer_lastio = time(NULL); server.repl_transfer_lastio = time(NULL);
server.repl_transfer_tmpfile = zstrdup(tmpfile); server.repl_transfer_tmpfile = zstrdup(tmpfile);
return;
error:
server.replstate = REDIS_REPL_CONNECT;
close(fd);
return;
}
int connectWithMaster(void) {
int fd;
fd = anetTcpNonBlockConnect(NULL,server.masterhost,server.masterport);
if (fd == -1) {
redisLog(REDIS_WARNING,"Unable to connect to MASTER: %s",
strerror(errno));
return REDIS_ERR;
}
if (aeCreateFileEvent(server.el,fd,AE_WRITABLE,syncWithMaster,NULL) ==
AE_ERR)
{
close(fd);
redisLog(REDIS_WARNING,"Can't create readable event for SYNC");
return REDIS_ERR;
}
server.repl_transfer_s = fd;
server.replstate = REDIS_REPL_CONNECTING;
return REDIS_OK; return REDIS_OK;
} }
...@@ -517,8 +517,8 @@ void replicationCron(void) { ...@@ -517,8 +517,8 @@ void replicationCron(void) {
/* Check if we should connect to a MASTER */ /* Check if we should connect to a MASTER */
if (server.replstate == REDIS_REPL_CONNECT) { if (server.replstate == REDIS_REPL_CONNECT) {
redisLog(REDIS_NOTICE,"Connecting to MASTER..."); redisLog(REDIS_NOTICE,"Connecting to MASTER...");
if (syncWithMaster() == REDIS_OK) { if (connectWithMaster() == REDIS_OK) {
redisLog(REDIS_NOTICE,"MASTER <-> SLAVE sync started: SYNC sent"); redisLog(REDIS_NOTICE,"MASTER <-> SLAVE sync started");
if (server.appendonly) rewriteAppendOnlyFileBackground(); if (server.appendonly) rewriteAppendOnlyFileBackground();
} }
} }
......
...@@ -249,8 +249,11 @@ void sremCommand(redisClient *c) { ...@@ -249,8 +249,11 @@ void sremCommand(redisClient *c) {
for (j = 2; j < c->argc; j++) { for (j = 2; j < c->argc; j++) {
if (setTypeRemove(set,c->argv[j])) { if (setTypeRemove(set,c->argv[j])) {
if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]);
deleted++; deleted++;
if (setTypeSize(set) == 0) {
dbDelete(c->db,c->argv[1]);
break;
}
} }
} }
if (deleted) { if (deleted) {
......
...@@ -810,11 +810,29 @@ void zaddGenericCommand(redisClient *c, int incr) { ...@@ -810,11 +810,29 @@ void zaddGenericCommand(redisClient *c, int incr) {
robj *ele; robj *ele;
robj *zobj; robj *zobj;
robj *curobj; robj *curobj;
double score, curscore = 0.0; double score = 0, *scores, curscore = 0.0;
int j, elements = (c->argc-2)/2;
int added = 0;
if (getDoubleFromObjectOrReply(c,c->argv[2],&score,NULL) != REDIS_OK) if (c->argc % 2) {
addReply(c,shared.syntaxerr);
return; return;
}
/* Start parsing all the scores, we need to emit any syntax error
* before executing additions to the sorted set, as the command should
* either execute fully or nothing at all. */
scores = zmalloc(sizeof(double)*elements);
for (j = 0; j < elements; j++) {
if (getDoubleFromObjectOrReply(c,c->argv[2+j*2],&scores[j],NULL)
!= REDIS_OK)
{
zfree(scores);
return;
}
}
/* Lookup the key and create the sorted set if does not exist. */
zobj = lookupKeyWrite(c->db,key); zobj = lookupKeyWrite(c->db,key);
if (zobj == NULL) { if (zobj == NULL) {
if (server.zset_max_ziplist_entries == 0 || if (server.zset_max_ziplist_entries == 0 ||
...@@ -828,111 +846,105 @@ void zaddGenericCommand(redisClient *c, int incr) { ...@@ -828,111 +846,105 @@ void zaddGenericCommand(redisClient *c, int incr) {
} else { } else {
if (zobj->type != REDIS_ZSET) { if (zobj->type != REDIS_ZSET) {
addReply(c,shared.wrongtypeerr); addReply(c,shared.wrongtypeerr);
zfree(scores);
return; return;
} }
} }
if (zobj->encoding == REDIS_ENCODING_ZIPLIST) { for (j = 0; j < elements; j++) {
unsigned char *eptr; score = scores[j];
/* Prefer non-encoded element when dealing with ziplists. */ if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {
ele = c->argv[3]; unsigned char *eptr;
if ((eptr = zzlFind(zobj->ptr,ele,&curscore)) != NULL) {
if (incr) { /* Prefer non-encoded element when dealing with ziplists. */
score += curscore; ele = c->argv[3+j*2];
if (isnan(score)) { if ((eptr = zzlFind(zobj->ptr,ele,&curscore)) != NULL) {
addReplyError(c,nanerr); if (incr) {
/* Don't need to check if the sorted set is empty, because score += curscore;
* we know it has at least one element. */ if (isnan(score)) {
return; addReplyError(c,nanerr);
/* Don't need to check if the sorted set is empty
* because we know it has at least one element. */
zfree(scores);
return;
}
} }
}
/* Remove and re-insert when score changed. */ /* Remove and re-insert when score changed. */
if (score != curscore) { if (score != curscore) {
zobj->ptr = zzlDelete(zobj->ptr,eptr); zobj->ptr = zzlDelete(zobj->ptr,eptr);
zobj->ptr = zzlInsert(zobj->ptr,ele,score);
signalModifiedKey(c->db,key);
server.dirty++;
}
} else {
/* Optimize: check if the element is too large or the list
* becomes too long *before* executing zzlInsert. */
zobj->ptr = zzlInsert(zobj->ptr,ele,score); zobj->ptr = zzlInsert(zobj->ptr,ele,score);
if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries)
zsetConvert(zobj,REDIS_ENCODING_SKIPLIST);
if (sdslen(ele->ptr) > server.zset_max_ziplist_value)
zsetConvert(zobj,REDIS_ENCODING_SKIPLIST);
signalModifiedKey(c->db,key); signalModifiedKey(c->db,key);
server.dirty++; server.dirty++;
if (!incr) added++;
} }
} else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {
zset *zs = zobj->ptr;
zskiplistNode *znode;
dictEntry *de;
if (incr) /* ZINCRBY */ ele = c->argv[3+j*2] = tryObjectEncoding(c->argv[3+j*2]);
addReplyDouble(c,score); de = dictFind(zs->dict,ele);
else /* ZADD */ if (de != NULL) {
addReply(c,shared.czero); curobj = dictGetEntryKey(de);
} else { curscore = *(double*)dictGetEntryVal(de);
/* Optimize: check if the element is too large or the list becomes
* too long *before* executing zzlInsert. */ if (incr) {
zobj->ptr = zzlInsert(zobj->ptr,ele,score); score += curscore;
if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries) if (isnan(score)) {
zsetConvert(zobj,REDIS_ENCODING_SKIPLIST); addReplyError(c,nanerr);
if (sdslen(ele->ptr) > server.zset_max_ziplist_value) /* Don't need to check if the sorted set is empty
zsetConvert(zobj,REDIS_ENCODING_SKIPLIST); * because we know it has at least one element. */
zfree(scores);
signalModifiedKey(c->db,key); return;
server.dirty++; }
if (incr) /* ZINCRBY */
addReplyDouble(c,score);
else /* ZADD */
addReply(c,shared.cone);
}
} else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {
zset *zs = zobj->ptr;
zskiplistNode *znode;
dictEntry *de;
ele = c->argv[3] = tryObjectEncoding(c->argv[3]);
de = dictFind(zs->dict,ele);
if (de != NULL) {
curobj = dictGetEntryKey(de);
curscore = *(double*)dictGetEntryVal(de);
if (incr) {
score += curscore;
if (isnan(score)) {
addReplyError(c,nanerr);
/* Don't need to check if the sorted set is empty, because
* we know it has at least one element. */
return;
} }
}
/* Remove and re-insert when score changed. We can safely delete /* Remove and re-insert when score changed. We can safely
* the key object from the skiplist, since the dictionary still has * delete the key object from the skiplist, since the
* a reference to it. */ * dictionary still has a reference to it. */
if (score != curscore) { if (score != curscore) {
redisAssert(zslDelete(zs->zsl,curscore,curobj)); redisAssert(zslDelete(zs->zsl,curscore,curobj));
znode = zslInsert(zs->zsl,score,curobj); znode = zslInsert(zs->zsl,score,curobj);
incrRefCount(curobj); /* Re-inserted in skiplist. */ incrRefCount(curobj); /* Re-inserted in skiplist. */
dictGetEntryVal(de) = &znode->score; /* Update score ptr. */ dictGetEntryVal(de) = &znode->score; /* Update score ptr. */
signalModifiedKey(c->db,key);
server.dirty++;
}
} else {
znode = zslInsert(zs->zsl,score,ele);
incrRefCount(ele); /* Inserted in skiplist. */
redisAssert(dictAdd(zs->dict,ele,&znode->score) == DICT_OK);
incrRefCount(ele); /* Added to dictionary. */
signalModifiedKey(c->db,key); signalModifiedKey(c->db,key);
server.dirty++; server.dirty++;
if (!incr) added++;
} }
if (incr) /* ZINCRBY */
addReplyDouble(c,score);
else /* ZADD */
addReply(c,shared.czero);
} else { } else {
znode = zslInsert(zs->zsl,score,ele); redisPanic("Unknown sorted set encoding");
incrRefCount(ele); /* Inserted in skiplist. */
redisAssert(dictAdd(zs->dict,ele,&znode->score) == DICT_OK);
incrRefCount(ele); /* Added to dictionary. */
signalModifiedKey(c->db,key);
server.dirty++;
if (incr) /* ZINCRBY */
addReplyDouble(c,score);
else /* ZADD */
addReply(c,shared.cone);
} }
} else {
redisPanic("Unknown sorted set encoding");
} }
zfree(scores);
if (incr) /* ZINCRBY */
addReplyDouble(c,score);
else /* ZADD */
addReplyLongLong(c,added);
} }
void zaddCommand(redisClient *c) { void zaddCommand(redisClient *c) {
...@@ -945,8 +957,8 @@ void zincrbyCommand(redisClient *c) { ...@@ -945,8 +957,8 @@ void zincrbyCommand(redisClient *c) {
void zremCommand(redisClient *c) { void zremCommand(redisClient *c) {
robj *key = c->argv[1]; robj *key = c->argv[1];
robj *ele = c->argv[2];
robj *zobj; robj *zobj;
int deleted = 0, j;
if ((zobj = lookupKeyWriteOrReply(c,key,shared.czero)) == NULL || if ((zobj = lookupKeyWriteOrReply(c,key,shared.czero)) == NULL ||
checkType(c,zobj,REDIS_ZSET)) return; checkType(c,zobj,REDIS_ZSET)) return;
...@@ -954,39 +966,48 @@ void zremCommand(redisClient *c) { ...@@ -954,39 +966,48 @@ void zremCommand(redisClient *c) {
if (zobj->encoding == REDIS_ENCODING_ZIPLIST) { if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *eptr; unsigned char *eptr;
if ((eptr = zzlFind(zobj->ptr,ele,NULL)) != NULL) { for (j = 2; j < c->argc; j++) {
zobj->ptr = zzlDelete(zobj->ptr,eptr); if ((eptr = zzlFind(zobj->ptr,c->argv[j],NULL)) != NULL) {
if (zzlLength(zobj->ptr) == 0) dbDelete(c->db,key); deleted++;
} else { zobj->ptr = zzlDelete(zobj->ptr,eptr);
addReply(c,shared.czero); if (zzlLength(zobj->ptr) == 0) {
return; dbDelete(c->db,key);
break;
}
}
} }
} else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) { } else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {
zset *zs = zobj->ptr; zset *zs = zobj->ptr;
dictEntry *de; dictEntry *de;
double score; double score;
de = dictFind(zs->dict,ele); for (j = 2; j < c->argc; j++) {
if (de != NULL) { de = dictFind(zs->dict,c->argv[j]);
/* Delete from the skiplist */ if (de != NULL) {
score = *(double*)dictGetEntryVal(de); deleted++;
redisAssert(zslDelete(zs->zsl,score,ele));
/* Delete from the skiplist */
/* Delete from the hash table */ score = *(double*)dictGetEntryVal(de);
dictDelete(zs->dict,ele); redisAssert(zslDelete(zs->zsl,score,c->argv[j]));
if (htNeedsResize(zs->dict)) dictResize(zs->dict);
if (dictSize(zs->dict) == 0) dbDelete(c->db,key); /* Delete from the hash table */
} else { dictDelete(zs->dict,c->argv[j]);
addReply(c,shared.czero); if (htNeedsResize(zs->dict)) dictResize(zs->dict);
return; if (dictSize(zs->dict) == 0) {
dbDelete(c->db,key);
break;
}
}
} }
} else { } else {
redisPanic("Unknown sorted set encoding"); redisPanic("Unknown sorted set encoding");
} }
signalModifiedKey(c->db,key); if (deleted) {
server.dirty++; signalModifiedKey(c->db,key);
addReply(c,shared.cone); server.dirty += deleted;
}
addReplyLongLong(c,deleted);
} }
void zremrangebyscoreCommand(redisClient *c) { void zremrangebyscoreCommand(redisClient *c) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment