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:
*/
int rewriteAppendOnlyFileBackground(void) {
pid_t childpid;
long long start;
if (server.bgrewritechildpid != -1) return REDIS_ERR;
if (server.ds_enabled != 0) {
redisLog(REDIS_WARNING,"BGREWRITEAOF called with diskstore enabled: AOF is not supported when diskstore is enabled. Operation not performed.");
return REDIS_ERR;
}
start = ustime();
if ((childpid = fork()) == 0) {
/* Child */
char tmpfile[256];
/* Child */
if (server.ipfd > 0) close(server.ipfd);
if (server.sofd > 0) close(server.sofd);
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid());
......@@ -585,6 +587,7 @@ int rewriteAppendOnlyFileBackground(void) {
}
} else {
/* Parent */
server.stat_fork_time = ustime()-start;
if (childpid == -1) {
redisLog(REDIS_WARNING,
"Can't rewrite append only file in background: fork: %s",
......
......@@ -180,7 +180,7 @@ void decrRefCount(void *obj) {
robj *o = obj;
if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
if (--(o->refcount) == 0) {
if (o->refcount == 1) {
switch(o->type) {
case REDIS_STRING: freeStringObject(o); break;
case REDIS_LIST: freeListObject(o); break;
......@@ -189,8 +189,9 @@ void decrRefCount(void *obj) {
case REDIS_HASH: freeHashObject(o); break;
default: redisPanic("Unknown object type"); break;
}
o->ptr = NULL; /* defensive programming. We'll see NULL in traces. */
zfree(o);
} else {
o->refcount--;
}
}
......
......@@ -482,6 +482,7 @@ werr:
int rdbSaveBackground(char *filename) {
pid_t childpid;
long long start;
if (server.bgsavechildpid != -1 ||
server.bgsavethread != (pthread_t) -1) return REDIS_ERR;
......@@ -493,6 +494,7 @@ int rdbSaveBackground(char *filename) {
return dsRdbSaveBackground(filename);
}
start = ustime();
if ((childpid = fork()) == 0) {
int retval;
......@@ -503,6 +505,7 @@ int rdbSaveBackground(char *filename) {
_exit((retval == REDIS_OK) ? 0 : 1);
} else {
/* Parent */
server.stat_fork_time = ustime()-start;
if (childpid == -1) {
redisLog(REDIS_WARNING,"Can't save in background: fork: %s",
strerror(errno));
......
......@@ -55,6 +55,7 @@ static struct config {
int hostport;
char *hostsocket;
long repeat;
long interval;
int dbnum;
int interactive;
int shutdown;
......@@ -87,9 +88,11 @@ static long long mstime(void) {
static void cliRefreshPrompt(void) {
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
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) {
return REDIS_OK;
}
static void cliPrintContextErrorAndExit() {
static void cliPrintContextError() {
if (context == NULL) return;
fprintf(stderr,"Error: %s\n",context->errstr);
exit(1);
}
static sds cliFormatReplyTTY(redisReply *r, char *prefix) {
......@@ -436,7 +438,8 @@ static int cliReadReply(int output_raw_strings) {
if (context->err == REDIS_ERR_EOF)
return REDIS_ERR;
}
cliPrintContextErrorAndExit();
cliPrintContextError();
exit(1);
return REDIS_ERR; /* avoid compiler warning */
}
......@@ -462,10 +465,7 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
size_t *argvlen;
int j, output_raw;
if (context == NULL) {
printf("Not connected, please use: connect <host> <port>\n");
return REDIS_OK;
}
if (context == NULL) return REDIS_ERR;
output_raw = 0;
if (!strcasecmp(command,"info") ||
......@@ -518,6 +518,8 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
cliRefreshPrompt();
}
}
if (config.interval) usleep(config.interval);
fflush(stdout); /* Make it grep friendly */
}
free(argvlen);
......@@ -553,6 +555,10 @@ static int parseOptions(int argc, char **argv) {
} else if (!strcmp(argv[i],"-r") && !lastarg) {
config.repeat = strtoll(argv[i+1],NULL,10);
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) {
config.dbnum = atoi(argv[i+1]);
i++;
......@@ -605,6 +611,8 @@ static void usage() {
" -s <socket> Server socket (overrides hostname and port)\n"
" -a <password> Password to use when connecting to the server\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"
" -x Read last argument from STDIN\n"
" -d <delimiter> Multi-bulk delimiter in for raw formatting (default: \\n)\n"
......@@ -616,6 +624,7 @@ static void usage() {
" cat /etc/passwd | redis-cli -x set mypasswd\n"
" redis-cli get mypasswd\n"
" redis-cli -r 100 lpush mylist x\n"
" redis-cli -r 100 -i 1 info | grep used_memory_human:\n"
"\n"
"When no command is given, redis-cli starts in interactive mode.\n"
"Type \"help\" in interactive mode for information on available commands.\n"
......@@ -681,14 +690,25 @@ static void repl() {
linenoiseClearScreen();
} else {
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);
/* If we still cannot send the command,
* print error and abort. */
if (cliSendCommand(argc,argv,1) != REDIS_OK)
cliPrintContextErrorAndExit();
/* If we still cannot send the command print error.
* We'll try to reconnect the next time. */
if (cliSendCommand(argc-skipargs,argv+skipargs,repeat)
!= REDIS_OK)
cliPrintContextError();
}
elapsed = mstime()-start_time;
if (elapsed >= 500) {
......@@ -726,6 +746,7 @@ int main(int argc, char **argv) {
config.hostport = 6379;
config.hostsocket = NULL;
config.repeat = 1;
config.interval = 0;
config.dbnum = 0;
config.interactive = 0;
config.shutdown = 0;
......@@ -741,11 +762,15 @@ int main(int argc, char **argv) {
argc -= firstarg;
argv += firstarg;
/* Try to connect */
if (cliConnect(0) != REDIS_OK) exit(1);
/* 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 */
if (cliConnect(0) != REDIS_OK) exit(1);
return noninteractive(argc,convertToSds(argc,argv));
}
......@@ -116,9 +116,9 @@ struct redisCommand redisCommandTable[] = {
{"sdiff",sdiffCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-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},
{"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},
{"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},
{"zremrangebyrank",zremrangebyrankCommand,4,0,NULL,1,1,1,0,0},
{"zunionstore",zunionstoreCommand,-4,REDIS_CMD_DENYOOM,zunionInterGetKeys,0,0,0,0,0},
......@@ -871,6 +871,7 @@ void initServerConfig() {
server.masterport = 6379;
server.master = NULL;
server.replstate = REDIS_REPL_NONE;
server.repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT;
server.repl_serve_stale_data = 1;
/* Double constants initialization */
......@@ -963,6 +964,7 @@ void initServer() {
server.stat_keyspace_misses = 0;
server.stat_keyspace_hits = 0;
server.stat_peak_memory = 0;
server.stat_fork_time = 0;
server.unixtime = time(NULL);
aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL);
if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE,
......@@ -1438,7 +1440,8 @@ sds genRedisInfoString(char *section) {
"keyspace_hits:%lld\r\n"
"keyspace_misses:%lld\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_numcommands,
server.stat_expiredkeys,
......@@ -1446,7 +1449,8 @@ sds genRedisInfoString(char *section) {
server.stat_keyspace_hits,
server.stat_keyspace_misses,
dictSize(server.pubsub_channels),
listLength(server.pubsub_patterns));
listLength(server.pubsub_patterns),
server.stat_fork_time);
}
/* Replication */
......
......@@ -41,7 +41,6 @@
#define REDIS_MAXIDLETIME (60*5) /* default client timeout */
#define REDIS_IOBUF_LEN 1024
#define REDIS_LOADBUF_LEN 1024
#define REDIS_STATIC_ARGS 8
#define REDIS_DEFAULT_DBNUM 16
#define REDIS_CONFIGLINE_MAX 1024
#define REDIS_MAX_SYNC_TIME 60 /* Slave can't take more to sync */
......@@ -155,8 +154,12 @@
/* Slave replication state - slave side */
#define REDIS_REPL_NONE 0 /* No active replication */
#define REDIS_REPL_CONNECT 1 /* Must connect to master */
#define REDIS_REPL_TRANSFER 2 /* Receiving .rdb from master */
#define REDIS_REPL_CONNECTED 3 /* Connected to master */
#define REDIS_REPL_CONNECTING 2 /* Connecting 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
* Note that in SEND_BULK and ONLINE state the slave receives new updates
......@@ -543,6 +546,7 @@ struct redisServer {
long long stat_keyspace_hits; /* number of successful lookups of keys */
long long stat_keyspace_misses; /* number of failed lookups of keys */
size_t stat_peak_memory; /* max used memory record */
long long stat_fork_time; /* time needed to perform latets fork() */
/* Configuration */
int verbosity;
int maxidletime;
......@@ -585,6 +589,7 @@ struct redisServer {
char *masterhost;
int masterport;
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 */
off_t repl_transfer_left; /* bytes left reading .rdb */
int repl_transfer_s; /* slave -> master SYNC socket */
......@@ -888,7 +893,6 @@ int fwriteBulkCount(FILE *fp, char prefix, int count);
/* Replication */
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc);
void replicationFeedMonitors(list *monitors, int dictid, robj **argv, int argc);
int syncWithMaster(void);
void updateSlavesWaitingBgsave(int bgsaveerr);
void replicationCron(void);
......
......@@ -10,38 +10,8 @@
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
listNode *ln;
listIter li;
int outc = 0, 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));
}
int j;
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);
while((ln = listNext(&li))) {
redisClient *slave = ln->value;
......@@ -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 */
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) {
robj *selectcmd;
......@@ -73,10 +45,9 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
addReply(slave,selectcmd);
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) {
......@@ -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
* from the master reply. */
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,
"I/O error reading bulk count from MASTER: %s",
strerror(errno));
replicationAbortSyncTransfer();
return;
goto error;
}
if (buf[0] == '-') {
redisLog(REDIS_WARNING,
"MASTER aborted replication with an error: %s",
buf+1);
replicationAbortSyncTransfer();
return;
goto error;
} else if (buf[0] == '\0') {
/* At this stage just a newline works as a PING in order to take
* the connection live. So we refresh our last interaction
......@@ -336,8 +306,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
return;
} 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?");
replicationAbortSyncTransfer();
return;
goto error;
}
server.repl_transfer_left = strtol(buf+1,NULL,10);
redisLog(REDIS_NOTICE,
......@@ -359,8 +328,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
server.repl_transfer_lastio = time(NULL);
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));
replicationAbortSyncTransfer();
return;
goto error;
}
server.repl_transfer_left -= nread;
/* Check if the transfer is now complete */
......@@ -391,48 +359,54 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
server.replstate = REDIS_REPL_CONNECTED;
redisLog(REDIS_NOTICE, "MASTER <-> SLAVE sync: Finished with success");
}
return;
error:
replicationAbortSyncTransfer();
return;
}
int syncWithMaster(void) {
char buf[1024], tmpfile[256], authcmd[1024];
int fd = anetTcpConnect(NULL,server.masterhost,server.masterport);
void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
char buf[1024], tmpfile[256];
int dfd, maxtries = 5;
REDIS_NOTUSED(el);
REDIS_NOTUSED(privdata);
REDIS_NOTUSED(mask);
if (fd == -1) {
redisLog(REDIS_WARNING,"Unable to connect to MASTER: %s",
strerror(errno));
return REDIS_ERR;
}
/* This event should only be triggered once since it is used to have a
* non-blocking connect(2) to the master. It has been triggered when this
* function is called, so we can delete it. */
aeDeleteFileEvent(server.el,fd,AE_WRITABLE);
/* AUTH with the master if required. */
if(server.masterauth) {
snprintf(authcmd, 1024, "AUTH %s\r\n", server.masterauth);
if (syncWrite(fd, authcmd, strlen(server.masterauth)+7, 5) == -1) {
close(fd);
char authcmd[1024];
size_t authlen;
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",
strerror(errno));
return REDIS_ERR;
goto error;
}
/* Read the AUTH result. */
if (syncReadLine(fd,buf,1024,3600) == -1) {
close(fd);
if (syncReadLine(fd,buf,1024,server.repl_syncio_timeout) == -1) {
redisLog(REDIS_WARNING,"I/O error reading auth result from MASTER: %s",
strerror(errno));
return REDIS_ERR;
goto error;
}
if (buf[0] != '+') {
close(fd);
redisLog(REDIS_WARNING,"Cannot AUTH to MASTER, is the masterauth password correct?");
return REDIS_ERR;
goto error;
}
}
/* Issue the SYNC command */
if (syncWrite(fd,"SYNC \r\n",7,5) == -1) {
close(fd);
if (syncWrite(fd,"SYNC \r\n",7,server.repl_syncio_timeout) == -1) {
redisLog(REDIS_WARNING,"I/O error writing to MASTER: %s",
strerror(errno));
return REDIS_ERR;
goto error;
}
/* Prepare a suitable temp file for bulk transfer */
......@@ -444,25 +418,51 @@ int syncWithMaster(void) {
sleep(1);
}
if (dfd == -1) {
close(fd);
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. */
if (aeCreateFileEvent(server.el, fd, AE_READABLE, readSyncBulkPayload, NULL)
if (aeCreateFileEvent(server.el,fd, AE_READABLE,readSyncBulkPayload,NULL)
== AE_ERR)
{
close(fd);
redisLog(REDIS_WARNING,"Can't create readable event for SYNC");
return REDIS_ERR;
goto error;
}
server.replstate = REDIS_REPL_TRANSFER;
server.repl_transfer_left = -1;
server.repl_transfer_s = fd;
server.repl_transfer_fd = dfd;
server.repl_transfer_lastio = time(NULL);
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;
}
......@@ -517,8 +517,8 @@ void replicationCron(void) {
/* Check if we should connect to a MASTER */
if (server.replstate == REDIS_REPL_CONNECT) {
redisLog(REDIS_NOTICE,"Connecting to MASTER...");
if (syncWithMaster() == REDIS_OK) {
redisLog(REDIS_NOTICE,"MASTER <-> SLAVE sync started: SYNC sent");
if (connectWithMaster() == REDIS_OK) {
redisLog(REDIS_NOTICE,"MASTER <-> SLAVE sync started");
if (server.appendonly) rewriteAppendOnlyFileBackground();
}
}
......
......@@ -249,8 +249,11 @@ void sremCommand(redisClient *c) {
for (j = 2; j < c->argc; j++) {
if (setTypeRemove(set,c->argv[j])) {
if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]);
deleted++;
if (setTypeSize(set) == 0) {
dbDelete(c->db,c->argv[1]);
break;
}
}
}
if (deleted) {
......
......@@ -810,11 +810,29 @@ void zaddGenericCommand(redisClient *c, int incr) {
robj *ele;
robj *zobj;
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;
}
/* 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);
if (zobj == NULL) {
if (server.zset_max_ziplist_entries == 0 ||
......@@ -828,22 +846,27 @@ void zaddGenericCommand(redisClient *c, int incr) {
} else {
if (zobj->type != REDIS_ZSET) {
addReply(c,shared.wrongtypeerr);
zfree(scores);
return;
}
}
for (j = 0; j < elements; j++) {
score = scores[j];
if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *eptr;
/* Prefer non-encoded element when dealing with ziplists. */
ele = c->argv[3];
ele = c->argv[3+j*2];
if ((eptr = zzlFind(zobj->ptr,ele,&curscore)) != NULL) {
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. */
/* Don't need to check if the sorted set is empty
* because we know it has at least one element. */
zfree(scores);
return;
}
}
......@@ -856,14 +879,9 @@ void zaddGenericCommand(redisClient *c, int incr) {
signalModifiedKey(c->db,key);
server.dirty++;
}
if (incr) /* ZINCRBY */
addReplyDouble(c,score);
else /* ZADD */
addReply(c,shared.czero);
} else {
/* Optimize: check if the element is too large or the list becomes
* too long *before* executing zzlInsert. */
/* Optimize: check if the element is too large or the list
* becomes too long *before* executing zzlInsert. */
zobj->ptr = zzlInsert(zobj->ptr,ele,score);
if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries)
zsetConvert(zobj,REDIS_ENCODING_SKIPLIST);
......@@ -872,18 +890,14 @@ void zaddGenericCommand(redisClient *c, int incr) {
signalModifiedKey(c->db,key);
server.dirty++;
if (incr) /* ZINCRBY */
addReplyDouble(c,score);
else /* ZADD */
addReply(c,shared.cone);
if (!incr) added++;
}
} else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {
zset *zs = zobj->ptr;
zskiplistNode *znode;
dictEntry *de;
ele = c->argv[3] = tryObjectEncoding(c->argv[3]);
ele = c->argv[3+j*2] = tryObjectEncoding(c->argv[3+j*2]);
de = dictFind(zs->dict,ele);
if (de != NULL) {
curobj = dictGetEntryKey(de);
......@@ -893,15 +907,16 @@ void zaddGenericCommand(redisClient *c, int 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. */
/* 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. We can safely delete
* the key object from the skiplist, since the dictionary still has
* a reference to it. */
/* Remove and re-insert when score changed. We can safely
* delete the key object from the skiplist, since the
* dictionary still has a reference to it. */
if (score != curscore) {
redisAssert(zslDelete(zs->zsl,curscore,curobj));
znode = zslInsert(zs->zsl,score,curobj);
......@@ -911,11 +926,6 @@ void zaddGenericCommand(redisClient *c, int incr) {
signalModifiedKey(c->db,key);
server.dirty++;
}
if (incr) /* ZINCRBY */
addReplyDouble(c,score);
else /* ZADD */
addReply(c,shared.czero);
} else {
znode = zslInsert(zs->zsl,score,ele);
incrRefCount(ele); /* Inserted in skiplist. */
......@@ -924,15 +934,17 @@ void zaddGenericCommand(redisClient *c, int incr) {
signalModifiedKey(c->db,key);
server.dirty++;
if (incr) /* ZINCRBY */
addReplyDouble(c,score);
else /* ZADD */
addReply(c,shared.cone);
if (!incr) added++;
}
} else {
redisPanic("Unknown sorted set encoding");
}
}
zfree(scores);
if (incr) /* ZINCRBY */
addReplyDouble(c,score);
else /* ZADD */
addReplyLongLong(c,added);
}
void zaddCommand(redisClient *c) {
......@@ -945,8 +957,8 @@ void zincrbyCommand(redisClient *c) {
void zremCommand(redisClient *c) {
robj *key = c->argv[1];
robj *ele = c->argv[2];
robj *zobj;
int deleted = 0, j;
if ((zobj = lookupKeyWriteOrReply(c,key,shared.czero)) == NULL ||
checkType(c,zobj,REDIS_ZSET)) return;
......@@ -954,39 +966,48 @@ void zremCommand(redisClient *c) {
if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *eptr;
if ((eptr = zzlFind(zobj->ptr,ele,NULL)) != NULL) {
for (j = 2; j < c->argc; j++) {
if ((eptr = zzlFind(zobj->ptr,c->argv[j],NULL)) != NULL) {
deleted++;
zobj->ptr = zzlDelete(zobj->ptr,eptr);
if (zzlLength(zobj->ptr) == 0) dbDelete(c->db,key);
} else {
addReply(c,shared.czero);
return;
if (zzlLength(zobj->ptr) == 0) {
dbDelete(c->db,key);
break;
}
}
}
} else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {
zset *zs = zobj->ptr;
dictEntry *de;
double score;
de = dictFind(zs->dict,ele);
for (j = 2; j < c->argc; j++) {
de = dictFind(zs->dict,c->argv[j]);
if (de != NULL) {
deleted++;
/* Delete from the skiplist */
score = *(double*)dictGetEntryVal(de);
redisAssert(zslDelete(zs->zsl,score,ele));
redisAssert(zslDelete(zs->zsl,score,c->argv[j]));
/* Delete from the hash table */
dictDelete(zs->dict,ele);
dictDelete(zs->dict,c->argv[j]);
if (htNeedsResize(zs->dict)) dictResize(zs->dict);
if (dictSize(zs->dict) == 0) dbDelete(c->db,key);
} else {
addReply(c,shared.czero);
return;
if (dictSize(zs->dict) == 0) {
dbDelete(c->db,key);
break;
}
}
}
} else {
redisPanic("Unknown sorted set encoding");
}
if (deleted) {
signalModifiedKey(c->db,key);
server.dirty++;
addReply(c,shared.cone);
server.dirty += deleted;
}
addReplyLongLong(c,deleted);
}
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