Compare commits

...
Author SHA1 Message Date
antirez c7ac181d52 server.current_client fix and minor refactoring.
Related to issue #2409.
2015-02-26 18:31:06 +01:00
antirez 832b0c7cce Improvements to PR #2425
1. Remove useless "cs" initialization.
2. Add a "select" var to capture a condition checked multiple times.
3. Avoid duplication of the same if (!copy) conditional.
4. Don't increment dirty if copy is given (no deletion is performed),
   otherwise we propagate MIGRATE when not needed.
2015-02-26 10:27:56 +01:00
Tommy Wangandantirez 7fda935ad3 Add last_dbid to migrateCachedSocket to avoid redundant SELECT
Avoid redundant SELECT calls when continuously migrating keys to
the same dbid within a target Redis instance.
2015-02-26 10:18:43 +01:00
antirez cc0d339bd1 utils/hashtable/rehashing.c test updated to use new API. 2015-02-25 13:02:04 +01:00
Salvatore Sanfilippo 126462ee36 Merge pull request #2405 from sisirkoppaka/unstable
rehashing.c: Fix compile error originating from SPOP rewrite
2015-02-25 12:52:23 +01:00
antirez 74354ceef5 Test: fixes a few tests after basic unit refactoring. 2015-02-25 10:37:52 +01:00
antirez 27c30b0e84 Cast sentlen to int before comparison wit bufpos.
This is safe since bufpos is small, inside the range of the local
client buffer.
2015-02-25 10:33:37 +01:00
antirez 954151f62b Test: split basic unit into string, incr, keyspace units. 2015-02-25 10:33:29 +01:00
Salvatore Sanfilippo 9454f7b3db Merge pull request #2050 from mattsta/bitops-no-overalloc
Bitops: Stop overallocating storage space on set
2015-02-25 10:18:07 +01:00
Salvatore Sanfilippo e00cb78f67 Merge pull request #2054 from mattsta/fix-set-sentinel-quorum
Sentinel: Add initial quorum bounds check
2015-02-25 10:09:40 +01:00
Matt Stancliffandantirez 47ab570441 Fix types broken by previous type cleanup
Revert some size_t back to off_t
Set reply_bytes needs to 64 bits everywhere
Revert bufpos to int since it's a max of 16k into buf[]
2015-02-24 17:39:59 +01:00
Salvatore Sanfilippo d83c810265 Merge pull request #2301 from mattsta/fix/lengths
Improve type correctness
2015-02-24 17:22:53 +01:00
Salvatore Sanfilippo 46bd13b806 Merge pull request #1966 from mattsta/fix-sentinel-info
Sentinel: Improve INFO command behavior
2015-02-24 17:20:09 +01:00
Salvatore Sanfilippo 3689a0582b Merge pull request #2273 from mattsta/improve/consistency/INFO/memory
Improve consistency of INFO MEMORY
2015-02-24 17:19:17 +01:00
Salvatore Sanfilippo 5ff00ac8c1 Merge pull request #2401 from mattsta/fix/quicklist/pop
Fix quicklist Pop()
2015-02-24 17:13:39 +01:00
antirez fc365a3a00 Change RENAME behavior when src and dst keys are the same.
Fixes issue #2392.
2015-02-23 11:24:24 +01:00
antirez 0aa5acc8f3 Give obvious instructions on how to bind all interfaces. 2015-02-19 22:47:32 +01:00
antirez 1024664247 Example redis.conf binds just to 127.0.0.1.
It's hard to pick a good approach here. A few arguments:

1) There are many exposed instances on the internet.
2) Changing the default when "bind" is not given is very dangerous,
   after an upgrade the server changes a fundamental behavior.
3) Usually Redis, when used in a proper way, will be protected *and*
   accessed often from other computers, so this new default is likely
   not what most people want.
4) However if users end with this default, they are using the example
   redis.conf: likely they are reading what is inside, and they'll see
   the warning.
2015-02-19 22:42:31 +01:00
Sisir Koppaka acb933a747 rehashing.c: Fix compile error originating from SPOP rewrite 2015-02-18 08:16:41 -05:00
Matt Stancliff 552e5908bb Fix quicklist tests for Pop()
Now the tests actually compare return values instead of just
verifying _something_ got returned.
2015-02-17 11:33:08 -05:00
John DoeandMatt Stancliff 395e11255c Fix quicklist Pop() result
Closes #2398
2015-02-17 11:31:08 -05:00
antirez cfe5eaaed6 Merge branch 'unstable' of github.com:/antirez/redis into unstable 2015-02-14 17:19:37 +01:00
antirez 41a1fb8186 Version bumped to 3.1.999 (3.2 alpha versions). 2015-02-14 17:19:17 +01:00
Salvatore Sanfilippo e5760987e9 Merge pull request #2393 from badboy/readme-fix
Readme fix
2015-02-13 15:55:44 +01:00
Jan-Erik Rediger 07a89654da Link files in README 2015-02-13 15:51:38 +01:00
antirez 9795ad1bc2 Fix README links again.
Apparently no refernece-style links supported in Github markdown.
2015-02-13 15:39:44 +01:00
Salvatore Sanfilippo 78c44ca646 Fix links into README. 2015-02-13 15:37:19 +01:00
antirez f916a589b5 README: give idea about what Redis is. 2015-02-13 15:34:22 +01:00
Jan-Erik Rediger 2576864ba7 Use code tags where necessary, escape special markers 2015-02-13 15:10:20 +01:00
antirez c414db2f98 A few more README markdown conversions. 2015-02-13 14:57:08 +01:00
antirez 097b3079b4 Fix README minor grammar issues. 2015-02-13 14:55:53 +01:00
antirez cac9a900cb README -> README.md. 2015-02-13 14:55:09 +01:00
antirez f638f045ce redis-cli --latency-dist: one gray more, and --mono support. 2015-02-13 14:38:21 +01:00
antirez 3101d2bffb redis-cli --latency-dist, hopefully better palette.
Less grays: more readable palette since usually we have a non linear
distribution of percentages and very near gray tones are hard to take
apart. Final part of the palette is gradient from yellow to red. The red
part is hardly reached because of usual distribution of latencies, but
shows up mainly when latencies are very high because of the logarithmic
scale, this is coherent to what people expect: red = bad.
2015-02-13 12:52:46 +01:00
Matt Stancliff 53c082ec39 Improve networking type correctness
read() and write() return ssize_t (signed long), not int.

For other offsets, we can use the unsigned size_t type instead
of a signed offset (since our replication offsets and buffer
positions are never negative).
2015-01-19 14:10:12 -05:00
Matt Stancliff f704360462 Improve RDB type correctness
It's possible large objects could be larger than 'int', so let's
upgrade all size counters to ssize_t.

This also fixes rdbSaveObject serialized bytes calculation.
Since entire serializations of data structures can be large,
so we don't want to limit their calculated size to a 32 bit signed max.

This commit increases object size calculation and
cascades the change back up to serializedlength printing.

Before:
127.0.0.1:6379> debug object hihihi
... encoding:quicklist serializedlength:-2147483559 ...

After:
127.0.0.1:6379> debug object hihihi
... encoding:quicklist serializedlength:2147483737 ...
2015-01-19 14:10:12 -05:00
Matt Stancliff 5a685f35a9 Add maxmemory limit to INFO MEMORY
Since we have the eviction policy, we should have the memory limit too.
2015-01-09 17:18:37 -05:00
Matt Stancliff 6741bb981c Improve consistency of INFO MEMORY fields
Adds used_memory_rss_human and used_memory_lua_human to match
all the other fields reporting human-readable memory too.
2015-01-09 17:18:37 -05:00
Matt Stancliff badf0f008b Bitops: Stop overallocating storage space on set
Previously the string was created empty then re-sized
to fit the offset, but sds resize causes the sds to
over-allocate by at least 1 MB (which is a lot when
you are operating at bit-level access).

This also improves the speed of initial sets by 2% to 6%
based on quick testing.

Patch logic provided by @oranagra

Fixes #1918
2014-12-11 10:54:21 -05:00
Matt Stancliff 391fc9b633 Sentinel: Improve INFO command behavior
Improvements:
  - Return empty string if asking for non-existing section (INFO foo)
  - Fix potential memory leak (caused by sdsempty() then returned if >2 args)
  - Clean up argument parsing
  - Allow "all" as valid section (same as "default" or zero args currently)
  - Move strcasecmp to end of evaluation chain in conditionals

Also, since we're C99, I moved some variable declarations to be closer
to where they are actually used (saves us from needing to free an empty info
if detect argument errors up front).

Closes #1915
Closes #1966
2014-12-11 10:49:16 -05:00
Matt Stancliff 6739ef4447 Sentinel: Add initial quorum bounds check
Fixes #2054
2014-11-20 16:30:17 -05:00
24 changed files with 677 additions and 561 deletions
+41 -23
View File
@@ -1,18 +1,35 @@
Where to find complete Redis documentation?
-------------------------------------------
This README is just a fast *quick start* document. You can find more detailed documentation at http://redis.io.
This README is just a fast "quick start" document. You can find more detailed
documentation at http://redis.io
What is Redis?
--------------
Redis is often referred as a *data structures* server. What this means is that Redis provides access to mutable data structures via a set of commands, which are send using a *server-client* model with TCP sockets and a simple protocol. So different processes can query and modify the same data structures in a shared way.
Data structures implemented into Redis have a few special properties:
* Redis cares to store them on disk, even if they are always served and modified into the server memory. This means that Redis is fast, but that is also non-volatile.
* Implementation of data structures stress on memory efficiency, so data structures inside Redis will likely use less memory compared to the same data structure modeled using an high level programming language.
* Redis offers a number of features that are natural to find into a database, like replication, tunable levels of durability, cluster, high availability.
Another good example is to think at Redis as a more complex version of memcached, where the opeations are not just SETs and GETs, but operations to work with complex data types like Lists, Sets, ordered data structures, and so forth.
If you want to know more, this is a list of selected starting points:
* Introduction to Redis data types. http://redis.io/topics/data-types-intro
* Try Redis directly inside your browser. http://try.redis.io
* The full list of Redis commands. http://redis.io/commands
* There is much more inside the Redis official documentation. http://redis.io/documentation
Building Redis
--------------
Redis can be compiled and used on Linux, OSX, OpenBSD, NetBSD, FreeBSD.
We support big endian and little endian architectures.
We support big endian and little endian architectures, and both 32 bit
and 64 bit systems.
It may compile on Solaris derived systems (for instance SmartOS) but our
support for this platform is "best effort" and Redis is not guaranteed to
work as well as in Linux, OSX, and *BSD there.
support for this platform is *best effort* and Redis is not guaranteed to
work as well as in Linux, OSX, and \*BSD there.
It is as simple as:
@@ -27,9 +44,10 @@ After building Redis is a good idea to test it, using:
% make test
Fixing build problems with dependencies or cached build options
--------
Redis has some dependencies which are included into the "deps" directory.
"make" does not rebuild dependencies automatically, even if something in the
---------
Redis has some dependencies which are included into the `deps` directory.
`make` does not rebuild dependencies automatically, even if something in the
source code of dependencies is changes.
When you update the source code with `git pull` or when code inside the
@@ -42,7 +60,7 @@ This will clean: jemalloc, lua, hiredis, linenoise.
Also if you force certain build options like 32bit target, no C compiler
optimizations (for debugging purposes), and other similar build time options,
those options are cached indefinitely until you issue a "make distclean"
those options are cached indefinitely until you issue a `make distclean`
command.
Fixing problems building 32 bit binaries
@@ -50,15 +68,14 @@ Fixing problems building 32 bit binaries
If after building Redis with a 32 bit target you need to rebuild it
with a 64 bit target, or the other way around, you need to perform a
"make distclean" in the root directory of the Redis distribution.
`make distclean` in the root directory of the Redis distribution.
In case of build errors when trying to build a 32 bit binary of Redis, try
the following steps:
* Install the packages libc6-dev-i386 (also try g++-multilib).
* Try using the following command line instead of "make 32bit":
make CFLAGS="-m32 -march=native" LDFLAGS="-m32"
* Try using the following command line instead of `make 32bit`:
`make CFLAGS="-m32 -march=native" LDFLAGS="-m32"`
Allocator
---------
@@ -126,11 +143,9 @@ then in another terminal try the following:
(integer) 1
redis> incr mycounter
(integer) 2
redis>
redis>
You can find the list of all the available commands here:
http://redis.io/commands
You can find the list of all the available commands at http://redis.io/commands.
Installing Redis
-----------------
@@ -139,7 +154,7 @@ In order to install Redis binaries into /usr/local/bin just use:
% make install
You can use "make PREFIX=/some/other/directory install" if you wish to use a
You can use `make PREFIX=/some/other/directory install` if you wish to use a
different destination.
Make install will just install binaries in your system, but will not configure
@@ -156,7 +171,7 @@ to run Redis properly as a background daemon that will start again on
system reboots.
You'll be able to stop and start Redis using the script named
/etc/init.d/redis_<portnumber>, for instance /etc/init.d/redis_6379.
`/etc/init.d/redis_<portnumber>`, for instance `/etc/init.d/redis_6379`.
Code contributions
---
@@ -164,10 +179,13 @@ Code contributions
Note: by contributing code to the Redis project in any form, including sending
a pull request via Github, a code fragment or patch via private email or
public discussion groups, you agree to release your code under the terms
of the BSD license that you can find in the COPYING file included in the Redis
of the BSD license that you can find in the [COPYING][1] file included in the Redis
source distribution.
Please see the CONTRIBUTING file in this source distribution for more
Please see the [CONTRIBUTING][2] file in this source distribution for more
information.
Enjoy!
[1]: https://github.com/antirez/redis/blob/unstable/COPYING
[2]: https://github.com/antirez/redis/blob/unstable/CONTRIBUTING
+45 -28
View File
@@ -30,26 +30,30 @@
# include /path/to/local.conf
# include /path/to/other.conf
################################ GENERAL #####################################
################################## NETWORK #####################################
# By default Redis does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
daemonize no
# If you run Redis from upstart or systemd, Redis can interact with your
# supervision tree. Options:
# supervised no - no supervision interaction
# supervised upstart - signal upstart by putting Redis into SIGSTOP mode
# supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET
# supervised auto - detect upstart or systemd method based on
# UPSTART_JOB or NOTIFY_SOCKET environment variables
# Note: these supervision methods only signal "process is ready."
# They do not enable continuous liveness pings back to your supervisor.
supervised no
# When running daemonized, Redis writes a pid file in /var/run/redis.pid by
# default. You can specify a custom pid file location here.
pidfile /var/run/redis.pid
# By default, if no "bind" configuration directive is specified, Redis listens
# for connections from all the network interfaces available on the server.
# It is possible to listen to just one or multiple selected interfaces using
# the "bind" configuration directive, followed by one or more IP addresses.
#
# Examples:
#
# bind 192.168.1.100 10.0.0.1
# bind 127.0.0.1 ::1
#
# ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the
# internet, binding to all the interfaces is dangerous and will expose the
# instance to everybody on the internet. So by default we uncomment the
# following bind directive, that will force Redis to listen only into
# the IPv4 lookback interface address (this means Redis will be able to
# accept connections only from clients running into the same computer it
# is running).
#
# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES
# JUST UNCOMMENT THE FOLLOWING LINE.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bind 127.0.0.1
# Accept connections on the specified port, default is 6379.
# If port 0 is specified Redis will not listen on a TCP socket.
@@ -64,16 +68,8 @@ port 6379
# in order to get the desired effect.
tcp-backlog 511
# By default Redis listens for connections from all the network interfaces
# available on the server. It is possible to listen to just one or multiple
# interfaces using the "bind" configuration directive, followed by one or
# more IP addresses.
# Unix socket.
#
# Examples:
#
# bind 192.168.1.100 10.0.0.1
# bind 127.0.0.1
# Specify the path for the Unix socket that will be used to listen for
# incoming connections. There is no default, so Redis will not listen
# on a unix socket when not specified.
@@ -100,6 +96,27 @@ timeout 0
# A reasonable value for this option is 60 seconds.
tcp-keepalive 0
################################# GENERAL #####################################
# By default Redis does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
daemonize no
# If you run Redis from upstart or systemd, Redis can interact with your
# supervision tree. Options:
# supervised no - no supervision interaction
# supervised upstart - signal upstart by putting Redis into SIGSTOP mode
# supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET
# supervised auto - detect upstart or systemd method based on
# UPSTART_JOB or NOTIFY_SOCKET environment variables
# Note: these supervision methods only signal "process is ready."
# They do not enable continuous liveness pings back to your supervisor.
supervised no
# When running daemonized, Redis writes a pid file in /var/run/redis.pid by
# default. You can specify a custom pid file location here.
pidfile /var/run/redis.pid
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
+2 -2
View File
@@ -391,7 +391,7 @@ int anetUnixNonBlockConnect(char *err, char *path)
* (unless error or EOF condition is encountered) */
int anetRead(int fd, char *buf, int count)
{
int nread, totlen = 0;
ssize_t nread, totlen = 0;
while(totlen != count) {
nread = read(fd,buf,count-totlen);
if (nread == 0) return totlen;
@@ -406,7 +406,7 @@ int anetRead(int fd, char *buf, int count)
* (unless error is encountered) */
int anetWrite(int fd, char *buf, int count)
{
int nwritten, totlen = 0;
ssize_t nwritten, totlen = 0;
while(totlen != count) {
nwritten = write(fd,buf,count-totlen);
if (nwritten == 0) return totlen;
+3 -5
View File
@@ -229,19 +229,17 @@ void setbitCommand(redisClient *c) {
return;
}
byte = bitoffset >> 3;
o = lookupKeyWrite(c->db,c->argv[1]);
if (o == NULL) {
o = createObject(REDIS_STRING,sdsempty());
o = createObject(REDIS_STRING,sdsnewlen(NULL, byte+1));
dbAdd(c->db,c->argv[1],o);
} else {
if (checkType(c,o,REDIS_STRING)) return;
o = dbUnshareStringValue(c->db,c->argv[1],o);
o->ptr = sdsgrowzero(o->ptr,byte+1);
}
/* Grow sds value to the right length if necessary */
byte = bitoffset >> 3;
o->ptr = sdsgrowzero(o->ptr,byte+1);
/* Get current values */
byteval = ((uint8_t*)o->ptr)[byte];
bit = 7 - (bitoffset & 0x7);
-2
View File
@@ -118,9 +118,7 @@ void processUnblockedClients(void) {
/* Process remaining data in the input buffer. */
if (c->querybuf && sdslen(c->querybuf) > 0) {
server.current_client = c;
processInputBuffer(c);
server.current_client = NULL;
}
}
}
+41 -28
View File
@@ -4362,11 +4362,12 @@ void restoreCommand(redisClient *c) {
typedef struct migrateCachedSocket {
int fd;
long last_dbid;
time_t last_use_time;
} migrateCachedSocket;
/* Return a TCP socket connected with the target instance, possibly returning
* a cached one.
/* Return a migrateCachedSocket containing a TCP socket connected with the
* target instance, possibly returning a cached one.
*
* This function is responsible of sending errors to the client if a
* connection can't be established. In this case -1 is returned.
@@ -4376,7 +4377,7 @@ typedef struct migrateCachedSocket {
* If the caller detects an error while using the socket, migrateCloseSocket()
* should be called so that the connection will be created from scratch
* the next time. */
int migrateGetSocket(redisClient *c, robj *host, robj *port, long timeout) {
migrateCachedSocket* migrateGetSocket(redisClient *c, robj *host, robj *port, long timeout) {
int fd;
sds name = sdsempty();
migrateCachedSocket *cs;
@@ -4389,7 +4390,7 @@ int migrateGetSocket(redisClient *c, robj *host, robj *port, long timeout) {
if (cs) {
sdsfree(name);
cs->last_use_time = server.unixtime;
return cs->fd;
return cs;
}
/* No cached socket, create one. */
@@ -4409,7 +4410,7 @@ int migrateGetSocket(redisClient *c, robj *host, robj *port, long timeout) {
sdsfree(name);
addReplyErrorFormat(c,"Can't connect to target node: %s",
server.neterr);
return -1;
return NULL;
}
anetEnableTcpNoDelay(server.neterr,fd);
@@ -4419,15 +4420,16 @@ int migrateGetSocket(redisClient *c, robj *host, robj *port, long timeout) {
addReplySds(c,
sdsnew("-IOERR error or timeout connecting to the client\r\n"));
close(fd);
return -1;
return NULL;
}
/* Add to the cache and return it to the caller. */
cs = zmalloc(sizeof(*cs));
cs->fd = fd;
cs->last_dbid = -1;
cs->last_use_time = server.unixtime;
dictAdd(server.migrate_cached_sockets,name,cs);
return fd;
return cs;
}
/* Free a migrate cached connection. */
@@ -4468,7 +4470,8 @@ void migrateCloseTimedoutSockets(void) {
/* MIGRATE host port key dbid timeout [COPY | REPLACE] */
void migrateCommand(redisClient *c) {
int fd, copy, replace, j;
migrateCachedSocket *cs;
int copy, replace, j;
long timeout;
long dbid;
long long ttl, expireat;
@@ -4510,15 +4513,20 @@ try_again:
}
/* Connect */
fd = migrateGetSocket(c,c->argv[1],c->argv[2],timeout);
if (fd == -1) return; /* error sent to the client by migrateGetSocket() */
cs = migrateGetSocket(c,c->argv[1],c->argv[2],timeout);
if (cs == NULL) return; /* error sent to the client by migrateGetSocket() */
rioInitWithBuffer(&cmd,sdsempty());
/* Send the SELECT command if the current DB is not already selected. */
int select = cs->last_dbid != dbid; /* Should we emit SELECT? */
if (select) {
redisAssertWithInfo(c,NULL,rioWriteBulkCount(&cmd,'*',2));
redisAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,"SELECT",6));
redisAssertWithInfo(c,NULL,rioWriteBulkLongLong(&cmd,dbid));
}
/* Create RESTORE payload and generate the protocol to call the command. */
rioInitWithBuffer(&cmd,sdsempty());
redisAssertWithInfo(c,NULL,rioWriteBulkCount(&cmd,'*',2));
redisAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,"SELECT",6));
redisAssertWithInfo(c,NULL,rioWriteBulkLongLong(&cmd,dbid));
expireat = getExpire(c->db,c->argv[3]);
if (expireat != -1) {
ttl = expireat-mstime();
@@ -4552,11 +4560,11 @@ try_again:
{
sds buf = cmd.io.buffer.ptr;
size_t pos = 0, towrite;
int nwritten = 0;
ssize_t nwritten = 0;
while ((towrite = sdslen(buf)-pos) > 0) {
towrite = (towrite > (64*1024) ? (64*1024) : towrite);
nwritten = syncWrite(fd,buf+pos,towrite,timeout);
nwritten = syncWrite(cs->fd,buf+pos,towrite,timeout);
if (nwritten != (signed)towrite) goto socket_wr_err;
pos += nwritten;
}
@@ -4568,28 +4576,33 @@ try_again:
char buf2[1024];
/* Read the two replies */
if (syncReadLine(fd, buf1, sizeof(buf1), timeout) <= 0)
if (select && syncReadLine(cs->fd, buf1, sizeof(buf1), timeout) <= 0)
goto socket_rd_err;
if (syncReadLine(fd, buf2, sizeof(buf2), timeout) <= 0)
if (syncReadLine(cs->fd, buf2, sizeof(buf2), timeout) <= 0)
goto socket_rd_err;
if (buf1[0] == '-' || buf2[0] == '-') {
if ((select && buf1[0] == '-') || buf2[0] == '-') {
/* On error assume that last_dbid is no longer valid. */
cs->last_dbid = -1;
addReplyErrorFormat(c,"Target instance replied with error: %s",
(buf1[0] == '-') ? buf1+1 : buf2+1);
(cs->last_dbid != dbid && buf1[0] == '-') ? buf1+1 : buf2+1);
} else {
/* Update the last_dbid in migrateCachedSocket */
cs->last_dbid = dbid;
robj *aux;
addReply(c,shared.ok);
if (!copy) {
/* No COPY option: remove the local key, signal the change. */
dbDelete(c->db,c->argv[3]);
signalModifiedKey(c->db,c->argv[3]);
}
addReply(c,shared.ok);
server.dirty++;
server.dirty++;
/* Translate MIGRATE as DEL for replication/AOF. */
aux = createStringObject("DEL",3);
rewriteClientCommandVector(c,2,aux,c->argv[3]);
decrRefCount(aux);
/* Translate MIGRATE as DEL for replication/AOF. */
aux = createStringObject("DEL",3);
rewriteClientCommandVector(c,2,aux,c->argv[3]);
decrRefCount(aux);
}
}
}
+9 -5
View File
@@ -688,16 +688,20 @@ void shutdownCommand(redisClient *c) {
void renameGenericCommand(redisClient *c, int nx) {
robj *o;
long long expire;
int samekey = 0;
/* To use the same key as src and dst is probably an error */
if (sdscmp(c->argv[1]->ptr,c->argv[2]->ptr) == 0) {
addReply(c,shared.sameobjecterr);
return;
}
/* When source and dest key is the same, no operation is performed,
* if the key exists, however we still return an error on unexisting key. */
if (sdscmp(c->argv[1]->ptr,c->argv[2]->ptr) == 0) samekey = 1;
if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr)) == NULL)
return;
if (samekey) {
addReply(c,nx ? shared.czero : shared.ok);
return;
}
incrRefCount(o);
expire = getExpire(c->db,c->argv[1]);
if (lookupKeyWrite(c->db,c->argv[2]) != NULL) {
+2 -2
View File
@@ -336,10 +336,10 @@ void debugCommand(redisClient *c) {
addReplyStatusFormat(c,
"Value at:%p refcount:%d "
"encoding:%s serializedlength:%lld "
"encoding:%s serializedlength:%zu "
"lru:%d lru_seconds_idle:%llu%s",
(void*)val, val->refcount,
strenc, (long long) rdbSavedObjectLen(val),
strenc, rdbSavedObjectLen(val),
val->lru, estimateObjectIdleTime(val)/1000, extra);
} else if (!strcasecmp(c->argv[1]->ptr,"sdslen") && c->argc == 3) {
dictEntry *de;
+15 -18
View File
@@ -797,7 +797,8 @@ void freeClientsInAsyncFreeQueue(void) {
void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
redisClient *c = privdata;
int nwritten = 0, totwritten = 0, objlen;
ssize_t nwritten = 0, totwritten = 0;
size_t objlen;
size_t objmem;
robj *o;
REDIS_NOTUSED(el);
@@ -812,7 +813,7 @@ void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
/* If the buffer was sent, set bufpos to zero to continue with
* the remainder of the reply. */
if (c->sentlen == c->bufpos) {
if ((int)c->sentlen == c->bufpos) {
c->bufpos = 0;
c->sentlen = 0;
}
@@ -1105,18 +1106,19 @@ int processMultibulkBuffer(redisClient *c) {
}
void processInputBuffer(redisClient *c) {
server.current_client = c;
/* Keep processing while there is something in the input buffer */
while(sdslen(c->querybuf)) {
/* Return if clients are paused. */
if (!(c->flags & REDIS_SLAVE) && clientsArePaused()) return;
if (!(c->flags & REDIS_SLAVE) && clientsArePaused()) break;
/* Immediately abort if the client is in the middle of something. */
if (c->flags & REDIS_BLOCKED) return;
if (c->flags & REDIS_BLOCKED) break;
/* REDIS_CLOSE_AFTER_REPLY closes the connection once the reply is
* written to the client. Make sure to not let the reply grow after
* this flag has been set (i.e. don't process more commands). */
if (c->flags & REDIS_CLOSE_AFTER_REPLY) return;
if (c->flags & REDIS_CLOSE_AFTER_REPLY) break;
/* Determine request type when unknown. */
if (!c->reqtype) {
@@ -1144,6 +1146,7 @@ void processInputBuffer(redisClient *c) {
resetClient(c);
}
}
server.current_client = NULL;
}
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
@@ -1153,7 +1156,6 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
REDIS_NOTUSED(el);
REDIS_NOTUSED(mask);
server.current_client = c;
readlen = REDIS_IOBUF_LEN;
/* If this is a multi bulk request, and we are processing a bulk reply
* that is large enough, try to maximize the probability that the query
@@ -1175,7 +1177,7 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
nread = read(fd, c->querybuf+qblen, readlen);
if (nread == -1) {
if (errno == EAGAIN) {
nread = 0;
return;
} else {
redisLog(REDIS_VERBOSE, "Reading from client: %s",strerror(errno));
freeClient(c);
@@ -1186,15 +1188,11 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
freeClient(c);
return;
}
if (nread) {
sdsIncrLen(c->querybuf,nread);
c->lastinteraction = server.unixtime;
if (c->flags & REDIS_MASTER) c->reploff += nread;
server.stat_net_input_bytes += nread;
} else {
server.current_client = NULL;
return;
}
sdsIncrLen(c->querybuf,nread);
c->lastinteraction = server.unixtime;
if (c->flags & REDIS_MASTER) c->reploff += nread;
server.stat_net_input_bytes += nread;
if (sdslen(c->querybuf) > server.client_max_querybuf_len) {
sds ci = catClientInfoString(sdsempty(),c), bytes = sdsempty();
@@ -1206,7 +1204,6 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
return;
}
processInputBuffer(c);
server.current_client = NULL;
}
void getClientsMaxBuffers(unsigned long *longest_output_list,
@@ -1621,7 +1618,7 @@ int checkClientOutputBufferLimits(redisClient *c) {
* called from contexts where the client can't be freed safely, i.e. from the
* lower level functions pushing data inside the client output buffers. */
void asyncCloseClientOnOutputBufferLimitReached(redisClient *c) {
redisAssert(c->reply_bytes < ULONG_MAX-(1024*64));
redisAssert(c->reply_bytes < SIZE_MAX-(1024*64));
if (c->reply_bytes == 0 || c->flags & REDIS_CLOSE_ASAP) return;
if (checkClientOutputBufferLimits(c)) {
sds client = catClientInfoString(sdsempty(),c);
+13 -2
View File
@@ -1372,7 +1372,7 @@ REDIS_STATIC void *_quicklistSaver(unsigned char *data, unsigned int sz) {
unsigned char *vstr;
if (data) {
vstr = zmalloc(sz);
memcpy(data, vstr, sz);
memcpy(vstr, data, sz);
return vstr;
}
return NULL;
@@ -1757,7 +1757,8 @@ int quicklistTest(int argc, char *argv[]) {
TEST("pop 1 string from 1") {
quicklist *ql = quicklistNew(-2, options[_i]);
quicklistPushHead(ql, genstr("hello", 331), 32);
char *populate = genstr("hello", 331);
quicklistPushHead(ql, populate, 32);
unsigned char *data;
unsigned int sz;
long long lv;
@@ -1765,6 +1766,9 @@ int quicklistTest(int argc, char *argv[]) {
quicklistPop(ql, QUICKLIST_HEAD, &data, &sz, &lv);
assert(data != NULL);
assert(sz == 32);
if (strcmp(populate, (char *)data))
ERR("Pop'd value (%.*s) didn't equal original value (%s)", sz,
data, populate);
zfree(data);
ql_verify(ql, 0, 0, 0, 0);
quicklistRelease(ql);
@@ -1797,6 +1801,9 @@ int quicklistTest(int argc, char *argv[]) {
assert(ret == 1);
assert(data != NULL);
assert(sz == 32);
if (strcmp(genstr("hello", 499 - i), (char *)data))
ERR("Pop'd value (%.*s) didn't equal original value (%s)",
sz, data, genstr("hello", 499 - i));
zfree(data);
}
ql_verify(ql, 0, 0, 0, 0);
@@ -1816,6 +1823,10 @@ int quicklistTest(int argc, char *argv[]) {
assert(ret == 1);
assert(data != NULL);
assert(sz == 32);
if (strcmp(genstr("hello", 499 - i), (char *)data))
ERR("Pop'd value (%.*s) didn't equal original value "
"(%s)",
sz, data, genstr("hello", 499 - i));
zfree(data);
} else {
assert(ret == 0);
+13 -13
View File
@@ -232,10 +232,10 @@ int rdbTryIntegerEncoding(char *s, size_t len, unsigned char *enc) {
return rdbEncodeInteger(value,enc);
}
int rdbSaveLzfBlob(rio *rdb, void *data, size_t compress_len,
size_t original_len) {
ssize_t rdbSaveLzfBlob(rio *rdb, void *data, size_t compress_len,
size_t original_len) {
unsigned char byte;
int n, nwritten = 0;
ssize_t n, nwritten = 0;
/* Data compressed! Let's save it on disk */
byte = (REDIS_RDB_ENCVAL<<6)|REDIS_RDB_ENC_LZF;
@@ -257,7 +257,7 @@ writeerr:
return -1;
}
int rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) {
ssize_t rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) {
size_t comprlen, outlen;
void *out;
@@ -270,7 +270,7 @@ int rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) {
zfree(out);
return 0;
}
size_t nwritten = rdbSaveLzfBlob(rdb, out, comprlen, len);
ssize_t nwritten = rdbSaveLzfBlob(rdb, out, comprlen, len);
zfree(out);
return nwritten;
}
@@ -315,9 +315,9 @@ err:
/* Save a string object as [len][data] on disk. If the object is a string
* representation of an integer value we try to save it in a special form */
int rdbSaveRawString(rio *rdb, unsigned char *s, size_t len) {
ssize_t rdbSaveRawString(rio *rdb, unsigned char *s, size_t len) {
int enclen;
int n, nwritten = 0;
ssize_t n, nwritten = 0;
/* Try integer encoding */
if (len <= 11) {
@@ -348,9 +348,9 @@ int rdbSaveRawString(rio *rdb, unsigned char *s, size_t len) {
}
/* Save a long long value as either an encoded string or a string. */
int rdbSaveLongLongAsStringObject(rio *rdb, long long value) {
ssize_t rdbSaveLongLongAsStringObject(rio *rdb, long long value) {
unsigned char buf[32];
int n, nwritten = 0;
ssize_t n, nwritten = 0;
int enclen = rdbEncodeInteger(value,buf);
if (enclen > 0) {
return rdbWriteRaw(rdb,buf,enclen);
@@ -542,8 +542,8 @@ int rdbLoadObjectType(rio *rdb) {
}
/* Save a Redis object. Returns -1 on error, number of bytes written on success. */
int rdbSaveObject(rio *rdb, robj *o) {
int n = 0, nwritten = 0;
ssize_t rdbSaveObject(rio *rdb, robj *o) {
ssize_t n = 0, nwritten = 0;
if (o->type == REDIS_STRING) {
/* Save a string value */
@@ -664,8 +664,8 @@ int rdbSaveObject(rio *rdb, robj *o) {
* the rdbSaveObject() function. Currently we use a trick to get
* this length with very little changes to the code. In the future
* we could switch to a faster solution. */
off_t rdbSavedObjectLen(robj *o) {
int len = rdbSaveObject(NULL,o);
size_t rdbSavedObjectLen(robj *o) {
ssize_t len = rdbSaveObject(NULL,o);
redisAssertWithInfo(NULL,o,len != -1);
return len;
}
+2 -3
View File
@@ -109,9 +109,8 @@ int rdbSaveBackground(char *filename);
int rdbSaveToSlavesSockets(void);
void rdbRemoveTempFile(pid_t childpid);
int rdbSave(char *filename);
int rdbSaveObject(rio *rdb, robj *o);
off_t rdbSavedObjectLen(robj *o);
off_t rdbSavedObjectPages(robj *o);
ssize_t rdbSaveObject(rio *rdb, robj *o);
size_t rdbSavedObjectLen(robj *o);
robj *rdbLoadObject(int type, rio *rdb);
void backgroundSaveDoneHandler(int exitcode, int bysignal);
int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime, long long now);
+2 -2
View File
@@ -86,7 +86,7 @@ typedef struct _client {
char **randptr; /* Pointers to :rand: strings inside the command buf */
size_t randlen; /* Number of pointers in client->randptr */
size_t randfree; /* Number of unused pointers in client->randptr */
unsigned int written; /* Bytes of 'obuf' already written */
size_t written; /* Bytes of 'obuf' already written */
long long start; /* Start time of a request */
long long latency; /* Request latency */
int pending; /* Number of pending requests (replies to consume) */
@@ -266,7 +266,7 @@ static void writeHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
if (sdslen(c->obuf) > c->written) {
void *ptr = c->obuf+c->written;
int nwritten = write(c->context->fd,ptr,sdslen(c->obuf)-c->written);
ssize_t nwritten = write(c->context->fd,ptr,sdslen(c->obuf)-c->written);
if (nwritten == -1) {
if (errno != EPIPE)
fprintf(stderr, "Writing to socket: %s\n", strerror(errno));
+17 -7
View File
@@ -64,6 +64,17 @@
#define REDIS_CLI_HISTFILE_ENV "REDISCLI_HISTFILE"
#define REDIS_CLI_HISTFILE_DEFAULT ".rediscli_history"
/* --latency-dist palettes. */
int spectrum_palette_color_size = 19;
int spectrum_palette_color[] = {0,233,234,235,237,239,241,243,245,247,144,143,142,184,226,214,208,202,196};
int spectrum_palette_mono_size = 13;
int spectrum_palette_mono[] = {0,233,234,235,237,239,241,243,245,247,249,251,253};
/* The actual palette in use. */
int *spectrum_palette;
int spectrum_palette_size;
static redisContext *context;
static struct config {
char *hostip;
@@ -780,6 +791,9 @@ static int parseOptions(int argc, char **argv) {
config.latency_mode = 1;
} else if (!strcmp(argv[i],"--latency-dist")) {
config.latency_dist_mode = 1;
} else if (!strcmp(argv[i],"--mono")) {
spectrum_palette = spectrum_palette_mono;
spectrum_palette_size = spectrum_palette_mono_size;
} else if (!strcmp(argv[i],"--latency-history")) {
config.latency_mode = 1;
config.latency_history = 1;
@@ -1128,13 +1142,6 @@ static void latencyMode(void) {
*--------------------------------------------------------------------------- */
#define LATENCY_DIST_DEFAULT_INTERVAL 1000 /* milliseconds. */
#define LATENCY_DIST_MIN_GRAY 233 /* Less than that is too hard to see gray. */
#define LATENCY_DIST_MAX_GRAY 255
#define LATENCY_DIST_GRAYS (LATENCY_DIST_MAX_GRAY-LATENCY_DIST_MIN_GRAY+1)
/* Gray palette. */
int spectrum_palette_size = 24;
int spectrum_palette[] = {0, 233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255};
/* Structure to store samples distribution. */
struct distsamples {
@@ -2200,6 +2207,9 @@ int main(int argc, char **argv) {
config.eval = NULL;
config.last_cmd_type = -1;
spectrum_palette = spectrum_palette_color;
spectrum_palette_size = spectrum_palette_color_size;
if (!isatty(fileno(stdout)) && (getenv("FAKETTY") == NULL))
config.output = OUTPUT_RAW;
else
+20 -5
View File
@@ -2736,9 +2736,13 @@ sds genRedisInfoString(char *section) {
char hmem[64];
char peak_hmem[64];
char total_system_hmem[64];
char used_memory_lua_hmem[64];
char used_memory_rss_hmem[64];
char maxmemory_hmem[64];
size_t zmalloc_used = zmalloc_used_memory();
size_t total_system_mem = server.system_memory_size;
char *evict_policy = maxmemoryToString();
long long memory_lua = (long long)lua_gc(server.lua,LUA_GCCOUNT,0)*1024;
/* Peak memory is updated from time to time by serverCron() so it
* may happen that the instantaneous value is slightly bigger than
@@ -2750,6 +2754,9 @@ sds genRedisInfoString(char *section) {
bytesToHuman(hmem,zmalloc_used);
bytesToHuman(peak_hmem,server.stat_peak_memory);
bytesToHuman(total_system_hmem,total_system_mem);
bytesToHuman(used_memory_lua_hmem,memory_lua);
bytesToHuman(used_memory_rss_hmem,server.resident_set_size);
bytesToHuman(maxmemory_hmem,server.maxmemory);
if (sections++) info = sdscat(info,"\r\n");
info = sdscatprintf(info,
@@ -2757,25 +2764,33 @@ sds genRedisInfoString(char *section) {
"used_memory:%zu\r\n"
"used_memory_human:%s\r\n"
"used_memory_rss:%zu\r\n"
"used_memory_rss_human:%s\r\n"
"used_memory_peak:%zu\r\n"
"used_memory_peak_human:%s\r\n"
"total_system_memory:%lu\r\n"
"total_system_memory_human:%s\r\n"
"used_memory_lua:%lld\r\n"
"used_memory_lua_human:%s\r\n"
"maxmemory:%lld\r\n"
"maxmemory_human:%s\r\n"
"maxmemory_policy:%s\r\n"
"mem_fragmentation_ratio:%.2f\r\n"
"mem_allocator:%s\r\n"
"maxmemory_policy:%s\r\n",
"mem_allocator:%s\r\n",
zmalloc_used,
hmem,
server.resident_set_size,
used_memory_rss_hmem,
server.stat_peak_memory,
peak_hmem,
(unsigned long)total_system_mem,
total_system_hmem,
((long long)lua_gc(server.lua,LUA_GCCOUNT,0))*1024LL,
memory_lua,
used_memory_lua_hmem,
server.maxmemory,
maxmemory_hmem,
evict_policy,
zmalloc_get_fragmentation_ratio(server.resident_set_size),
ZMALLOC_LIB,
evict_policy
ZMALLOC_LIB
);
}
+4 -4
View File
@@ -543,8 +543,8 @@ typedef struct redisClient {
int multibulklen; /* number of multi bulk arguments left to read */
long bulklen; /* length of bulk argument in multi bulk request */
list *reply;
unsigned long reply_bytes; /* Tot bytes of objects in reply list */
int sentlen; /* Amount of bytes already sent in the current
unsigned long long reply_bytes; /* Tot bytes of objects in reply list */
size_t sentlen; /* Amount of bytes already sent in the current
buffer or object being sent. */
time_t ctime; /* Client creation time */
time_t lastinteraction; /* time of the last interaction, used for timeout */
@@ -554,8 +554,8 @@ typedef struct redisClient {
int replstate; /* replication state if this is a slave */
int repl_put_online_on_ack; /* Install slave write handler on ACK. */
int repldbfd; /* replication DB file descriptor */
off_t repldboff; /* replication DB file offset */
off_t repldbsize; /* replication DB file size */
off_t repldboff; /* replication DB file offset */
off_t repldbsize; /* replication DB file size */
sds replpreamble; /* replication DB preamble. */
long long reploff; /* replication offset if this is our master */
long long repl_ack_off; /* replication ack offset, if this is a slave */
+19 -7
View File
@@ -2741,6 +2741,12 @@ void sentinelCommand(redisClient *c) {
!= REDIS_OK) return;
if (getLongFromObjectOrReply(c,c->argv[4],&port,"Invalid port")
!= REDIS_OK) return;
if (quorum <= 0) {
addReplyError(c, "Quorum must be 1 or greater.");
return;
}
/* Make sure the IP field is actually a valid IP before passing it
* to createSentinelRedisInstance(), otherwise we may trigger a
* DNS lookup at runtime. */
@@ -2856,24 +2862,30 @@ numargserr:
/* SENTINEL INFO [section] */
void sentinelInfoCommand(redisClient *c) {
char *section = c->argc == 2 ? c->argv[1]->ptr : "default";
sds info = sdsempty();
int defsections = !strcasecmp(section,"default");
int sections = 0;
if (c->argc > 2) {
addReply(c,shared.syntaxerr);
return;
}
if (!strcasecmp(section,"server") || defsections) {
int defsections = 0, allsections = 0;
char *section = c->argc == 2 ? c->argv[1]->ptr : NULL;
if (section) {
allsections = !strcasecmp(section,"all");
defsections = !strcasecmp(section,"default");
} else {
defsections = 1;
}
int sections = 0;
sds info = sdsempty();
if (defsections || allsections || !strcasecmp(section,"server")) {
if (sections++) info = sdscat(info,"\r\n");
sds serversection = genRedisInfoString("server");
info = sdscatlen(info,serversection,sdslen(serversection));
sdsfree(serversection);
}
if (!strcasecmp(section,"sentinel") || defsections) {
if (defsections || allsections || !strcasecmp(section,"sentinel")) {
dictIterator *di;
dictEntry *de;
int master_id = 0;
+1 -1
View File
@@ -206,7 +206,7 @@ void setrangeCommand(redisClient *c) {
if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)
return;
o = createObject(REDIS_STRING,sdsempty());
o = createObject(REDIS_STRING,sdsnewlen(NULL, offset+sdslen(value)));
dbAdd(c->db,c->argv[1],o);
} else {
size_t olen;
+1 -1
View File
@@ -1 +1 @@
#define REDIS_VERSION "2.9.999"
#define REDIS_VERSION "3.1.999"
+3 -1
View File
@@ -16,8 +16,10 @@ set ::all_tests {
unit/dump
unit/auth
unit/protocol
unit/basic
unit/keyspace
unit/scan
unit/type/string
unit/type/incr
unit/type/list
unit/type/list-2
unit/type/list-3
+249
View File
@@ -0,0 +1,249 @@
start_server {tags {"keyspace"}} {
test {DEL against a single item} {
r set x foo
assert {[r get x] eq "foo"}
r del x
r get x
} {}
test {Vararg DEL} {
r set foo1 a
r set foo2 b
r set foo3 c
list [r del foo1 foo2 foo3 foo4] [r mget foo1 foo2 foo3]
} {3 {{} {} {}}}
test {KEYS with pattern} {
foreach key {key_x key_y key_z foo_a foo_b foo_c} {
r set $key hello
}
lsort [r keys foo*]
} {foo_a foo_b foo_c}
test {KEYS to get all keys} {
lsort [r keys *]
} {foo_a foo_b foo_c key_x key_y key_z}
test {DBSIZE} {
r dbsize
} {6}
test {DEL all keys} {
foreach key [r keys *] {r del $key}
r dbsize
} {0}
test "DEL against expired key" {
r debug set-active-expire 0
r setex keyExpire 1 valExpire
after 1100
assert_equal 0 [r del keyExpire]
r debug set-active-expire 1
}
test {EXISTS} {
set res {}
r set newkey test
append res [r exists newkey]
r del newkey
append res [r exists newkey]
} {10}
test {Zero length value in key. SET/GET/EXISTS} {
r set emptykey {}
set res [r get emptykey]
append res [r exists emptykey]
r del emptykey
append res [r exists emptykey]
} {10}
test {Commands pipelining} {
set fd [r channel]
puts -nonewline $fd "SET k1 xyzk\r\nGET k1\r\nPING\r\n"
flush $fd
set res {}
append res [string match OK* [r read]]
append res [r read]
append res [string match PONG* [r read]]
format $res
} {1xyzk1}
test {Non existing command} {
catch {r foobaredcommand} err
string match ERR* $err
} {1}
test {RENAME basic usage} {
r set mykey hello
r rename mykey mykey1
r rename mykey1 mykey2
r get mykey2
} {hello}
test {RENAME source key should no longer exist} {
r exists mykey
} {0}
test {RENAME against already existing key} {
r set mykey a
r set mykey2 b
r rename mykey2 mykey
set res [r get mykey]
append res [r exists mykey2]
} {b0}
test {RENAMENX basic usage} {
r del mykey
r del mykey2
r set mykey foobar
r renamenx mykey mykey2
set res [r get mykey2]
append res [r exists mykey]
} {foobar0}
test {RENAMENX against already existing key} {
r set mykey foo
r set mykey2 bar
r renamenx mykey mykey2
} {0}
test {RENAMENX against already existing key (2)} {
set res [r get mykey]
append res [r get mykey2]
} {foobar}
test {RENAME against non existing source key} {
catch {r rename nokey foobar} err
format $err
} {ERR*}
test {RENAME where source and dest key are the same (existing)} {
r set mykey foo
r rename mykey mykey
} {OK}
test {RENAMENX where source and dest key are the same (existing)} {
r set mykey foo
r renamenx mykey mykey
} {0}
test {RENAME where source and dest key are the same (non existing)} {
r del mykey
catch {r rename mykey mykey} err
format $err
} {ERR*}
test {RENAME with volatile key, should move the TTL as well} {
r del mykey mykey2
r set mykey foo
r expire mykey 100
assert {[r ttl mykey] > 95 && [r ttl mykey] <= 100}
r rename mykey mykey2
assert {[r ttl mykey2] > 95 && [r ttl mykey2] <= 100}
}
test {RENAME with volatile key, should not inherit TTL of target key} {
r del mykey mykey2
r set mykey foo
r set mykey2 bar
r expire mykey2 100
assert {[r ttl mykey] == -1 && [r ttl mykey2] > 0}
r rename mykey mykey2
r ttl mykey2
} {-1}
test {DEL all keys again (DB 0)} {
foreach key [r keys *] {
r del $key
}
r dbsize
} {0}
test {DEL all keys again (DB 1)} {
r select 10
foreach key [r keys *] {
r del $key
}
set res [r dbsize]
r select 9
format $res
} {0}
test {MOVE basic usage} {
r set mykey foobar
r move mykey 10
set res {}
lappend res [r exists mykey]
lappend res [r dbsize]
r select 10
lappend res [r get mykey]
lappend res [r dbsize]
r select 9
format $res
} [list 0 0 foobar 1]
test {MOVE against key existing in the target DB} {
r set mykey hello
r move mykey 10
} {0}
test {MOVE against non-integer DB (#1428)} {
r set mykey hello
catch {r move mykey notanumber} e
set e
} {*ERR*index out of range}
test {SET/GET keys in different DBs} {
r set a hello
r set b world
r select 10
r set a foo
r set b bared
r select 9
set res {}
lappend res [r get a]
lappend res [r get b]
r select 10
lappend res [r get a]
lappend res [r get b]
r select 9
format $res
} {hello world foo bared}
test {RANDOMKEY} {
r flushdb
r set foo x
r set bar y
set foo_seen 0
set bar_seen 0
for {set i 0} {$i < 100} {incr i} {
set rkey [r randomkey]
if {$rkey eq {foo}} {
set foo_seen 1
}
if {$rkey eq {bar}} {
set bar_seen 1
}
}
list $foo_seen $bar_seen
} {1 1}
test {RANDOMKEY against empty DB} {
r flushdb
r randomkey
} {}
test {RANDOMKEY regression 1} {
r flushdb
r set x 10
r del x
r randomkey
} {}
test {KEYS * two times with long key, Github issue #1208} {
r flushdb
r set dlskeriewrioeuwqoirueioqwrueoqwrueqw test
r keys *
r keys *
} {dlskeriewrioeuwqoirueioqwrueoqwrueqw}
}
+147
View File
@@ -0,0 +1,147 @@
start_server {tags {"incr"}} {
test {INCR against non existing key} {
set res {}
append res [r incr novar]
append res [r get novar]
} {11}
test {INCR against key created by incr itself} {
r incr novar
} {2}
test {INCR against key originally set with SET} {
r set novar 100
r incr novar
} {101}
test {INCR over 32bit value} {
r set novar 17179869184
r incr novar
} {17179869185}
test {INCRBY over 32bit value with over 32bit increment} {
r set novar 17179869184
r incrby novar 17179869184
} {34359738368}
test {INCR fails against key with spaces (left)} {
r set novar " 11"
catch {r incr novar} err
format $err
} {ERR*}
test {INCR fails against key with spaces (right)} {
r set novar "11 "
catch {r incr novar} err
format $err
} {ERR*}
test {INCR fails against key with spaces (both)} {
r set novar " 11 "
catch {r incr novar} err
format $err
} {ERR*}
test {INCR fails against a key holding a list} {
r rpush mylist 1
catch {r incr mylist} err
r rpop mylist
format $err
} {WRONGTYPE*}
test {DECRBY over 32bit value with over 32bit increment, negative res} {
r set novar 17179869184
r decrby novar 17179869185
} {-1}
test {INCR uses shared objects in the 0-9999 range} {
r set foo -1
r incr foo
assert {[r object refcount foo] > 1}
r set foo 9998
r incr foo
assert {[r object refcount foo] > 1}
r incr foo
assert {[r object refcount foo] == 1}
}
test {INCR can modify objects in-place} {
r set foo 20000
r incr foo
assert {[r object refcount foo] == 1}
set old [lindex [split [r debug object foo]] 1]
r incr foo
set new [lindex [split [r debug object foo]] 1]
assert {[string range $old 0 2] eq "at:"}
assert {[string range $new 0 2] eq "at:"}
assert {$old eq $new}
}
test {INCRBYFLOAT against non existing key} {
r del novar
list [roundFloat [r incrbyfloat novar 1]] \
[roundFloat [r get novar]] \
[roundFloat [r incrbyfloat novar 0.25]] \
[roundFloat [r get novar]]
} {1 1 1.25 1.25}
test {INCRBYFLOAT against key originally set with SET} {
r set novar 1.5
roundFloat [r incrbyfloat novar 1.5]
} {3}
test {INCRBYFLOAT over 32bit value} {
r set novar 17179869184
r incrbyfloat novar 1.5
} {17179869185.5}
test {INCRBYFLOAT over 32bit value with over 32bit increment} {
r set novar 17179869184
r incrbyfloat novar 17179869184
} {34359738368}
test {INCRBYFLOAT fails against key with spaces (left)} {
set err {}
r set novar " 11"
catch {r incrbyfloat novar 1.0} err
format $err
} {ERR*valid*}
test {INCRBYFLOAT fails against key with spaces (right)} {
set err {}
r set novar "11 "
catch {r incrbyfloat novar 1.0} err
format $err
} {ERR*valid*}
test {INCRBYFLOAT fails against key with spaces (both)} {
set err {}
r set novar " 11 "
catch {r incrbyfloat novar 1.0} err
format $err
} {ERR*valid*}
test {INCRBYFLOAT fails against a key holding a list} {
r del mylist
set err {}
r rpush mylist 1
catch {r incrbyfloat mylist 1.0} err
r del mylist
format $err
} {WRONGTYPE*}
test {INCRBYFLOAT does not allow NaN or Infinity} {
r set foo 0
set err {}
catch {r incrbyfloat foo +inf} err
set err
# p.s. no way I can force NaN to test it from the API because
# there is no way to increment / decrement by infinity nor to
# perform divisions.
} {ERR*would produce*}
test {INCRBYFLOAT decrement} {
r set foo 1
roundFloat [r incrbyfloat foo -1.1]
} {-0.1}
}
@@ -1,9 +1,4 @@
start_server {tags {"basic"}} {
test {DEL all keys to start with a clean DB} {
foreach key [r keys *] {r del $key}
r dbsize
} {0}
start_server {tags {"string"}} {
test {SET and GET an item} {
r set x foobar
r get x
@@ -14,38 +9,6 @@ start_server {tags {"basic"}} {
r get x
} {}
test {DEL against a single item} {
r del x
r get x
} {}
test {Vararg DEL} {
r set foo1 a
r set foo2 b
r set foo3 c
list [r del foo1 foo2 foo3 foo4] [r mget foo1 foo2 foo3]
} {3 {{} {} {}}}
test {KEYS with pattern} {
foreach key {key_x key_y key_z foo_a foo_b foo_c} {
r set $key hello
}
lsort [r keys foo*]
} {foo_a foo_b foo_c}
test {KEYS to get all keys} {
lsort [r keys *]
} {foo_a foo_b foo_c key_x key_y key_z}
test {DBSIZE} {
r dbsize
} {6}
test {DEL all keys} {
foreach key [r keys *] {r del $key}
r dbsize
} {0}
test {Very big payload in GET/SET} {
set buf [string repeat "abcd" 1000000]
r set foo $buf
@@ -75,6 +38,7 @@ start_server {tags {"basic"}} {
} {}
test {SET 10000 numeric keys and access all them in reverse order} {
r flushdb
set err {}
for {set x 0} {$x < 10000} {incr x} {
r set $x $x
@@ -90,157 +54,11 @@ start_server {tags {"basic"}} {
set _ $err
} {}
test {DBSIZE should be 10101 now} {
test {DBSIZE should be 10000 now} {
r dbsize
} {10101}
} {10000}
}
test {INCR against non existing key} {
set res {}
append res [r incr novar]
append res [r get novar]
} {11}
test {INCR against key created by incr itself} {
r incr novar
} {2}
test {INCR against key originally set with SET} {
r set novar 100
r incr novar
} {101}
test {INCR over 32bit value} {
r set novar 17179869184
r incr novar
} {17179869185}
test {INCRBY over 32bit value with over 32bit increment} {
r set novar 17179869184
r incrby novar 17179869184
} {34359738368}
test {INCR fails against key with spaces (left)} {
r set novar " 11"
catch {r incr novar} err
format $err
} {ERR*}
test {INCR fails against key with spaces (right)} {
r set novar "11 "
catch {r incr novar} err
format $err
} {ERR*}
test {INCR fails against key with spaces (both)} {
r set novar " 11 "
catch {r incr novar} err
format $err
} {ERR*}
test {INCR fails against a key holding a list} {
r rpush mylist 1
catch {r incr mylist} err
r rpop mylist
format $err
} {WRONGTYPE*}
test {DECRBY over 32bit value with over 32bit increment, negative res} {
r set novar 17179869184
r decrby novar 17179869185
} {-1}
test {INCR uses shared objects in the 0-9999 range} {
r set foo -1
r incr foo
assert {[r object refcount foo] > 1}
r set foo 9998
r incr foo
assert {[r object refcount foo] > 1}
r incr foo
assert {[r object refcount foo] == 1}
}
test {INCR can modify objects in-place} {
r set foo 20000
r incr foo
assert {[r object refcount foo] == 1}
set old [lindex [split [r debug object foo]] 1]
r incr foo
set new [lindex [split [r debug object foo]] 1]
assert {[string range $old 0 2] eq "at:"}
assert {[string range $new 0 2] eq "at:"}
assert {$old eq $new}
}
test {INCRBYFLOAT against non existing key} {
r del novar
list [roundFloat [r incrbyfloat novar 1]] \
[roundFloat [r get novar]] \
[roundFloat [r incrbyfloat novar 0.25]] \
[roundFloat [r get novar]]
} {1 1 1.25 1.25}
test {INCRBYFLOAT against key originally set with SET} {
r set novar 1.5
roundFloat [r incrbyfloat novar 1.5]
} {3}
test {INCRBYFLOAT over 32bit value} {
r set novar 17179869184
r incrbyfloat novar 1.5
} {17179869185.5}
test {INCRBYFLOAT over 32bit value with over 32bit increment} {
r set novar 17179869184
r incrbyfloat novar 17179869184
} {34359738368}
test {INCRBYFLOAT fails against key with spaces (left)} {
set err {}
r set novar " 11"
catch {r incrbyfloat novar 1.0} err
format $err
} {ERR*valid*}
test {INCRBYFLOAT fails against key with spaces (right)} {
set err {}
r set novar "11 "
catch {r incrbyfloat novar 1.0} err
format $err
} {ERR*valid*}
test {INCRBYFLOAT fails against key with spaces (both)} {
set err {}
r set novar " 11 "
catch {r incrbyfloat novar 1.0} err
format $err
} {ERR*valid*}
test {INCRBYFLOAT fails against a key holding a list} {
r del mylist
set err {}
r rpush mylist 1
catch {r incrbyfloat mylist 1.0} err
r del mylist
format $err
} {WRONGTYPE*}
test {INCRBYFLOAT does not allow NaN or Infinity} {
r set foo 0
set err {}
catch {r incrbyfloat foo +inf} err
set err
# p.s. no way I can force NaN to test it from the API because
# there is no way to increment / decrement by infinity nor to
# perform divisions.
} {ERR*would produce*}
test {INCRBYFLOAT decrement} {
r set foo 1
roundFloat [r incrbyfloat foo -1.1]
} {-0.1}
test "SETNX target key missing" {
r del novar
assert_equal 1 [r setnx novar foobared]
@@ -284,172 +102,6 @@ start_server {tags {"basic"}} {
assert_equal 20 [r get x]
}
test "DEL against expired key" {
r debug set-active-expire 0
r setex keyExpire 1 valExpire
after 1100
assert_equal 0 [r del keyExpire]
r debug set-active-expire 1
}
test {EXISTS} {
set res {}
r set newkey test
append res [r exists newkey]
r del newkey
append res [r exists newkey]
} {10}
test {Zero length value in key. SET/GET/EXISTS} {
r set emptykey {}
set res [r get emptykey]
append res [r exists emptykey]
r del emptykey
append res [r exists emptykey]
} {10}
test {Commands pipelining} {
set fd [r channel]
puts -nonewline $fd "SET k1 xyzk\r\nGET k1\r\nPING\r\n"
flush $fd
set res {}
append res [string match OK* [r read]]
append res [r read]
append res [string match PONG* [r read]]
format $res
} {1xyzk1}
test {Non existing command} {
catch {r foobaredcommand} err
string match ERR* $err
} {1}
test {RENAME basic usage} {
r set mykey hello
r rename mykey mykey1
r rename mykey1 mykey2
r get mykey2
} {hello}
test {RENAME source key should no longer exist} {
r exists mykey
} {0}
test {RENAME against already existing key} {
r set mykey a
r set mykey2 b
r rename mykey2 mykey
set res [r get mykey]
append res [r exists mykey2]
} {b0}
test {RENAMENX basic usage} {
r del mykey
r del mykey2
r set mykey foobar
r renamenx mykey mykey2
set res [r get mykey2]
append res [r exists mykey]
} {foobar0}
test {RENAMENX against already existing key} {
r set mykey foo
r set mykey2 bar
r renamenx mykey mykey2
} {0}
test {RENAMENX against already existing key (2)} {
set res [r get mykey]
append res [r get mykey2]
} {foobar}
test {RENAME against non existing source key} {
catch {r rename nokey foobar} err
format $err
} {ERR*}
test {RENAME where source and dest key is the same} {
catch {r rename mykey mykey} err
format $err
} {ERR*}
test {RENAME with volatile key, should move the TTL as well} {
r del mykey mykey2
r set mykey foo
r expire mykey 100
assert {[r ttl mykey] > 95 && [r ttl mykey] <= 100}
r rename mykey mykey2
assert {[r ttl mykey2] > 95 && [r ttl mykey2] <= 100}
}
test {RENAME with volatile key, should not inherit TTL of target key} {
r del mykey mykey2
r set mykey foo
r set mykey2 bar
r expire mykey2 100
assert {[r ttl mykey] == -1 && [r ttl mykey2] > 0}
r rename mykey mykey2
r ttl mykey2
} {-1}
test {DEL all keys again (DB 0)} {
foreach key [r keys *] {
r del $key
}
r dbsize
} {0}
test {DEL all keys again (DB 1)} {
r select 10
foreach key [r keys *] {
r del $key
}
set res [r dbsize]
r select 9
format $res
} {0}
test {MOVE basic usage} {
r set mykey foobar
r move mykey 10
set res {}
lappend res [r exists mykey]
lappend res [r dbsize]
r select 10
lappend res [r get mykey]
lappend res [r dbsize]
r select 9
format $res
} [list 0 0 foobar 1]
test {MOVE against key existing in the target DB} {
r set mykey hello
r move mykey 10
} {0}
test {MOVE against non-integer DB (#1428)} {
r set mykey hello
catch {r move mykey notanumber} e
set e
} {*ERR*index out of range}
test {SET/GET keys in different DBs} {
r set a hello
r set b world
r select 10
r set a foo
r set b bared
r select 9
set res {}
lappend res [r get a]
lappend res [r get b]
r select 10
lappend res [r get a]
lappend res [r get b]
r select 9
format $res
} {hello world foo bared}
test {MGET} {
r flushdb
r set foo BAR
@@ -467,37 +119,8 @@ start_server {tags {"basic"}} {
r mget foo baazz bar myset
} {BAR {} FOO {}}
test {RANDOMKEY} {
r flushdb
r set foo x
r set bar y
set foo_seen 0
set bar_seen 0
for {set i 0} {$i < 100} {incr i} {
set rkey [r randomkey]
if {$rkey eq {foo}} {
set foo_seen 1
}
if {$rkey eq {bar}} {
set bar_seen 1
}
}
list $foo_seen $bar_seen
} {1 1}
test {RANDOMKEY against empty DB} {
r flushdb
r randomkey
} {}
test {RANDOMKEY regression 1} {
r flushdb
r set x 10
r del x
r randomkey
} {}
test {GETSET (set new value)} {
r del foo
list [r getset foo xyz] [r get foo]
} {{} xyz}
@@ -792,13 +415,6 @@ start_server {tags {"basic"}} {
assert {$ttl <= 10 && $ttl > 5}
}
test {KEYS * two times with long key, Github issue #1208} {
r flushdb
r set dlskeriewrioeuwqoirueioqwrueoqwrueqw test
r keys *
r keys *
} {dlskeriewrioeuwqoirueioqwrueoqwrueqw}
test {GETRANGE with huge ranges, Github issue #1844} {
r set foo bar
r getrange foo 0 4294967297
+23 -13
View File
@@ -68,26 +68,30 @@ int sortPointers(const void *a, const void *b) {
return la-lb;
}
void stressGetKeys(dict *d, int times) {
void stressGetKeys(dict *d, int times, int *perfect_run, int *approx_run) {
int j;
dictEntry **des = zmalloc(sizeof(dictEntry*)*dictSize(d));
for (j = 0; j < times; j++) {
int requested = rand() % (dictSize(d)+1);
int returned = dictGetRandomKeys(d, des, requested);
if (requested != returned) {
printf("*** ERROR! Req: %d, Ret: %d\n", requested, returned);
exit(1);
}
int returned = dictGetSomeKeys(d, des, requested);
int dup = 0;
qsort(des,returned,sizeof(dictEntry*),sortPointers);
if (returned > 1) {
int i;
for (i = 0; i < returned-1; i++) {
if (des[i] == des[i+1]) {
printf("*** ERROR! Duplicated element detected\n");
exit(1);
}
if (des[i] == des[i+1]) dup++;
}
}
if (requested == returned && dup == 0) {
(*perfect_run)++;
} else {
(*approx_run)++;
printf("Requested, returned, duplicated: %d %d %d\n",
requested, returned, dup);
}
}
zfree(des);
}
@@ -113,18 +117,24 @@ int main(void) {
dictRelease(d);
d = dictCreate(&dictTypeTest,NULL);
printf("Getkeys stress test\n");
printf("Stress testing dictGetSomeKeys\n");
int perfect_run = 0, approx_run = 0;
for (i = 0; i < MAX2; i++) {
dictAdd(d,(void*)i,NULL);
stressGetKeys(d,100);
stressGetKeys(d,100,&perfect_run,&approx_run);
}
for (i = 0; i < MAX2; i++) {
dictDelete(d,(void*)i);
dictResize(d);
stressGetKeys(d,100);
stressGetKeys(d,100,&perfect_run,&approx_run);
}
printf("dictGetSomeKey, %d perfect runs, %d approximated runs\n",
perfect_run, approx_run);
dictRelease(d);
printf("TEST PASSED!\n");