Compare commits
41
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7ac181d52 | ||
|
|
832b0c7cce | ||
|
|
7fda935ad3 | ||
|
|
cc0d339bd1 | ||
|
|
126462ee36 | ||
|
|
74354ceef5 | ||
|
|
27c30b0e84 | ||
|
|
954151f62b | ||
|
|
9454f7b3db | ||
|
|
e00cb78f67 | ||
|
|
47ab570441 | ||
|
|
d83c810265 | ||
|
|
46bd13b806 | ||
|
|
3689a0582b | ||
|
|
5ff00ac8c1 | ||
|
|
fc365a3a00 | ||
|
|
0aa5acc8f3 | ||
|
|
1024664247 | ||
|
|
acb933a747 | ||
|
|
552e5908bb | ||
|
|
395e11255c | ||
|
|
cfe5eaaed6 | ||
|
|
41a1fb8186 | ||
|
|
e5760987e9 | ||
|
|
07a89654da | ||
|
|
9795ad1bc2 | ||
|
|
78c44ca646 | ||
|
|
f916a589b5 | ||
|
|
2576864ba7 | ||
|
|
c414db2f98 | ||
|
|
097b3079b4 | ||
|
|
cac9a900cb | ||
|
|
f638f045ce | ||
|
|
3101d2bffb | ||
|
|
53c082ec39 | ||
|
|
f704360462 | ||
|
|
5a685f35a9 | ||
|
|
6741bb981c | ||
|
|
badf0f008b | ||
|
|
391fc9b633 | ||
|
|
6739ef4447 |
+41
-23
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1 +1 @@
|
||||
#define REDIS_VERSION "2.9.999"
|
||||
#define REDIS_VERSION "3.1.999"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
@@ -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
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user