Compare commits

..
Author SHA1 Message Date
antirez c1d10e8bb7 No-sync: some info in the example redis.conf comments. 2015-02-13 08:57:15 +01:00
antirez ede98af9da No-sync: fixed missing conditional in syncCommand(). 2015-02-13 08:57:15 +01:00
antirez de6f74838a No-sync: avoid master to be marked as PRE-PSYNC.
replicationCreateMasterClient() will mark it as such if we don't set a
fake initial replication offset.
2015-02-13 08:57:15 +01:00
antirez abbfbcf161 Experimental no-sync replication mode. 2015-02-13 08:57:15 +01:00
26 changed files with 658 additions and 680 deletions
+23 -41
View File
@@ -1,35 +1,18 @@
This README is just a fast *quick start* document. You can find more detailed documentation at http://redis.io.
Where to find complete Redis documentation?
-------------------------------------------
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
This README is just a fast "quick start" document. You can find more detailed
documentation at http://redis.io
Building Redis
--------------
Redis can be compiled and used on Linux, OSX, OpenBSD, NetBSD, FreeBSD.
We support big endian and little endian architectures, and both 32 bit
and 64 bit systems.
We support big endian and little endian architectures.
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:
@@ -44,10 +27,9 @@ 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
@@ -60,7 +42,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
@@ -68,14 +50,15 @@ 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
---------
@@ -143,9 +126,11 @@ 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 at http://redis.io/commands.
You can find the list of all the available commands here:
http://redis.io/commands
Installing Redis
-----------------
@@ -154,7 +139,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
@@ -171,7 +156,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
---
@@ -179,13 +164,10 @@ 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][1] file included in the Redis
of the BSD license that you can find in the COPYING file included in the Redis
source distribution.
Please see the [CONTRIBUTING][2] file in this source distribution for more
Please see the CONTRIBUTING 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
+60 -45
View File
@@ -30,30 +30,26 @@
# include /path/to/local.conf
# include /path/to/other.conf
################################## NETWORK #####################################
################################ GENERAL #####################################
# 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
# 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
# Accept connections on the specified port, default is 6379.
# If port 0 is specified Redis will not listen on a TCP socket.
@@ -68,8 +64,16 @@ port 6379
# in order to get the desired effect.
tcp-backlog 511
# Unix socket.
# 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.
#
# 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.
@@ -96,27 +100,6 @@ 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)
@@ -317,6 +300,38 @@ repl-diskless-sync-delay 5
#
# repl-ping-slave-period 10
# WARNING: This feature is experimental, just an evaluation draft.
#
# No-sync replication is a special, inconistent, mode of operation for slaves
# that is sometimes useful in caching scenarios.
#
# In this setup slaves, when connecting to masters, don't ask for the pre
# existing state of the key space, so no synchronization with the current
# master data is performed at all. However the slaves will get all the new
# stream of writes receiving the master, starting from the moment the slave
# connects.
#
# This means that:
# 1) There is no consistency between master and slaves operating in this mode.
# 2) It is possible that keys with an expire in the master will never expire
# in the slave (because the slave may miss the EXPIRE command).
# 3) Keys updated with non-idempotent commands may end with different values.
#
# However if your workload is about idempotent updates (for example using
# just the SET command), and no-sync slaves are configured in order to evict
# data on their own using maxmemory and a suitable policy, there are certain
# advantages:
#
# 1) Slaves are always available (assuming they are configured to serve stale
# data), since reconnecting with the master has no cost.
# 2) Masters don't have to persist on disk and/or stream an RDB file to the
# slaves, slaves are just accepted ASAP and put online.
#
# To enable, simply set repl-no-sync to yes. By default this feature is
# disabled.
#
# repl-no-sync no
# The following option sets the replication timeout for:
#
# 1) Bulk transfer I/O during SYNC, from the point of view of slave.
+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)
{
ssize_t nread, totlen = 0;
int 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)
{
ssize_t nwritten, totlen = 0;
int nwritten, totlen = 0;
while(totlen != count) {
nwritten = write(fd,buf,count-totlen);
if (nwritten == 0) return totlen;
+5 -3
View File
@@ -229,17 +229,19 @@ void setbitCommand(redisClient *c) {
return;
}
byte = bitoffset >> 3;
o = lookupKeyWrite(c->db,c->argv[1]);
if (o == NULL) {
o = createObject(REDIS_STRING,sdsnewlen(NULL, byte+1));
o = createObject(REDIS_STRING,sdsempty());
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,7 +118,9 @@ 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;
}
}
}
+28 -41
View File
@@ -4362,12 +4362,11 @@ void restoreCommand(redisClient *c) {
typedef struct migrateCachedSocket {
int fd;
long last_dbid;
time_t last_use_time;
} migrateCachedSocket;
/* Return a migrateCachedSocket containing a TCP socket connected with the
* target instance, possibly returning a cached one.
/* Return 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.
@@ -4377,7 +4376,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. */
migrateCachedSocket* migrateGetSocket(redisClient *c, robj *host, robj *port, long timeout) {
int migrateGetSocket(redisClient *c, robj *host, robj *port, long timeout) {
int fd;
sds name = sdsempty();
migrateCachedSocket *cs;
@@ -4390,7 +4389,7 @@ migrateCachedSocket* migrateGetSocket(redisClient *c, robj *host, robj *port, lo
if (cs) {
sdsfree(name);
cs->last_use_time = server.unixtime;
return cs;
return cs->fd;
}
/* No cached socket, create one. */
@@ -4410,7 +4409,7 @@ migrateCachedSocket* migrateGetSocket(redisClient *c, robj *host, robj *port, lo
sdsfree(name);
addReplyErrorFormat(c,"Can't connect to target node: %s",
server.neterr);
return NULL;
return -1;
}
anetEnableTcpNoDelay(server.neterr,fd);
@@ -4420,16 +4419,15 @@ migrateCachedSocket* migrateGetSocket(redisClient *c, robj *host, robj *port, lo
addReplySds(c,
sdsnew("-IOERR error or timeout connecting to the client\r\n"));
close(fd);
return NULL;
return -1;
}
/* 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 cs;
return fd;
}
/* Free a migrate cached connection. */
@@ -4470,8 +4468,7 @@ void migrateCloseTimedoutSockets(void) {
/* MIGRATE host port key dbid timeout [COPY | REPLACE] */
void migrateCommand(redisClient *c) {
migrateCachedSocket *cs;
int copy, replace, j;
int fd, copy, replace, j;
long timeout;
long dbid;
long long ttl, expireat;
@@ -4513,20 +4510,15 @@ try_again:
}
/* Connect */
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));
}
fd = migrateGetSocket(c,c->argv[1],c->argv[2],timeout);
if (fd == -1) return; /* error sent to the client by migrateGetSocket() */
/* 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();
@@ -4560,11 +4552,11 @@ try_again:
{
sds buf = cmd.io.buffer.ptr;
size_t pos = 0, towrite;
ssize_t nwritten = 0;
int nwritten = 0;
while ((towrite = sdslen(buf)-pos) > 0) {
towrite = (towrite > (64*1024) ? (64*1024) : towrite);
nwritten = syncWrite(cs->fd,buf+pos,towrite,timeout);
nwritten = syncWrite(fd,buf+pos,towrite,timeout);
if (nwritten != (signed)towrite) goto socket_wr_err;
pos += nwritten;
}
@@ -4576,33 +4568,28 @@ try_again:
char buf2[1024];
/* Read the two replies */
if (select && syncReadLine(cs->fd, buf1, sizeof(buf1), timeout) <= 0)
if (syncReadLine(fd, buf1, sizeof(buf1), timeout) <= 0)
goto socket_rd_err;
if (syncReadLine(cs->fd, buf2, sizeof(buf2), timeout) <= 0)
if (syncReadLine(fd, buf2, sizeof(buf2), timeout) <= 0)
goto socket_rd_err;
if ((select && buf1[0] == '-') || buf2[0] == '-') {
/* On error assume that last_dbid is no longer valid. */
cs->last_dbid = -1;
if (buf1[0] == '-' || buf2[0] == '-') {
addReplyErrorFormat(c,"Target instance replied with error: %s",
(cs->last_dbid != dbid && buf1[0] == '-') ? buf1+1 : buf2+1);
(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]);
server.dirty++;
/* Translate MIGRATE as DEL for replication/AOF. */
aux = createStringObject("DEL",3);
rewriteClientCommandVector(c,2,aux,c->argv[3]);
decrRefCount(aux);
}
addReply(c,shared.ok);
server.dirty++;
/* Translate MIGRATE as DEL for replication/AOF. */
aux = createStringObject("DEL",3);
rewriteClientCommandVector(c,2,aux,c->argv[3]);
decrRefCount(aux);
}
}
+4
View File
@@ -276,6 +276,10 @@ void loadServerConfigFromString(char *config) {
if ((server.repl_diskless_sync = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"repl-no-sync") && argc==2) {
if ((server.repl_no_sync = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"repl-diskless-sync-delay") && argc==2) {
server.repl_diskless_sync_delay = atoi(argv[1]);
if (server.repl_diskless_sync_delay < 0) {
+5 -9
View File
@@ -688,20 +688,16 @@ void shutdownCommand(redisClient *c) {
void renameGenericCommand(redisClient *c, int nx) {
robj *o;
long long expire;
int samekey = 0;
/* 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;
/* 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;
}
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:%zu "
"encoding:%s serializedlength:%lld "
"lru:%d lru_seconds_idle:%llu%s",
(void*)val, val->refcount,
strenc, rdbSavedObjectLen(val),
strenc, (long long) rdbSavedObjectLen(val),
val->lru, estimateObjectIdleTime(val)/1000, extra);
} else if (!strcasecmp(c->argv[1]->ptr,"sdslen") && c->argc == 3) {
dictEntry *de;
+22 -16
View File
@@ -673,7 +673,10 @@ void freeClient(redisClient *c) {
*
* Note that before doing this we make sure that the client is not in
* some unexpected state, by checking its flags. */
if (server.master && c->flags & REDIS_MASTER) {
if (server.master &&
c->flags & REDIS_MASTER &&
server.repl_no_sync == 0)
{
redisLog(REDIS_WARNING,"Connection with master lost.");
if (!(c->flags & (REDIS_CLOSE_AFTER_REPLY|
REDIS_CLOSE_ASAP|
@@ -797,8 +800,7 @@ void freeClientsInAsyncFreeQueue(void) {
void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
redisClient *c = privdata;
ssize_t nwritten = 0, totwritten = 0;
size_t objlen;
int nwritten = 0, totwritten = 0, objlen;
size_t objmem;
robj *o;
REDIS_NOTUSED(el);
@@ -813,7 +815,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 ((int)c->sentlen == c->bufpos) {
if (c->sentlen == c->bufpos) {
c->bufpos = 0;
c->sentlen = 0;
}
@@ -1106,19 +1108,18 @@ 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()) break;
if (!(c->flags & REDIS_SLAVE) && clientsArePaused()) return;
/* Immediately abort if the client is in the middle of something. */
if (c->flags & REDIS_BLOCKED) break;
if (c->flags & REDIS_BLOCKED) return;
/* 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) break;
if (c->flags & REDIS_CLOSE_AFTER_REPLY) return;
/* Determine request type when unknown. */
if (!c->reqtype) {
@@ -1146,7 +1147,6 @@ void processInputBuffer(redisClient *c) {
resetClient(c);
}
}
server.current_client = NULL;
}
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
@@ -1156,6 +1156,7 @@ 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
@@ -1177,7 +1178,7 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
nread = read(fd, c->querybuf+qblen, readlen);
if (nread == -1) {
if (errno == EAGAIN) {
return;
nread = 0;
} else {
redisLog(REDIS_VERBOSE, "Reading from client: %s",strerror(errno));
freeClient(c);
@@ -1188,11 +1189,15 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
freeClient(c);
return;
}
sdsIncrLen(c->querybuf,nread);
c->lastinteraction = server.unixtime;
if (c->flags & REDIS_MASTER) c->reploff += nread;
server.stat_net_input_bytes += nread;
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;
}
if (sdslen(c->querybuf) > server.client_max_querybuf_len) {
sds ci = catClientInfoString(sdsempty(),c), bytes = sdsempty();
@@ -1204,6 +1209,7 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
return;
}
processInputBuffer(c);
server.current_client = NULL;
}
void getClientsMaxBuffers(unsigned long *longest_output_list,
@@ -1618,7 +1624,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 < SIZE_MAX-(1024*64));
redisAssert(c->reply_bytes < ULONG_MAX-(1024*64));
if (c->reply_bytes == 0 || c->flags & REDIS_CLOSE_ASAP) return;
if (checkClientOutputBufferLimits(c)) {
sds client = catClientInfoString(sdsempty(),c);
+2 -13
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(vstr, data, sz);
memcpy(data, vstr, sz);
return vstr;
}
return NULL;
@@ -1757,8 +1757,7 @@ int quicklistTest(int argc, char *argv[]) {
TEST("pop 1 string from 1") {
quicklist *ql = quicklistNew(-2, options[_i]);
char *populate = genstr("hello", 331);
quicklistPushHead(ql, populate, 32);
quicklistPushHead(ql, genstr("hello", 331), 32);
unsigned char *data;
unsigned int sz;
long long lv;
@@ -1766,9 +1765,6 @@ 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);
@@ -1801,9 +1797,6 @@ 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);
@@ -1823,10 +1816,6 @@ 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);
}
ssize_t rdbSaveLzfBlob(rio *rdb, void *data, size_t compress_len,
size_t original_len) {
int rdbSaveLzfBlob(rio *rdb, void *data, size_t compress_len,
size_t original_len) {
unsigned char byte;
ssize_t n, nwritten = 0;
int 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;
}
ssize_t rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) {
int rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) {
size_t comprlen, outlen;
void *out;
@@ -270,7 +270,7 @@ ssize_t rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) {
zfree(out);
return 0;
}
ssize_t nwritten = rdbSaveLzfBlob(rdb, out, comprlen, len);
size_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 */
ssize_t rdbSaveRawString(rio *rdb, unsigned char *s, size_t len) {
int rdbSaveRawString(rio *rdb, unsigned char *s, size_t len) {
int enclen;
ssize_t n, nwritten = 0;
int n, nwritten = 0;
/* Try integer encoding */
if (len <= 11) {
@@ -348,9 +348,9 @@ ssize_t rdbSaveRawString(rio *rdb, unsigned char *s, size_t len) {
}
/* Save a long long value as either an encoded string or a string. */
ssize_t rdbSaveLongLongAsStringObject(rio *rdb, long long value) {
int rdbSaveLongLongAsStringObject(rio *rdb, long long value) {
unsigned char buf[32];
ssize_t n, nwritten = 0;
int 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. */
ssize_t rdbSaveObject(rio *rdb, robj *o) {
ssize_t n = 0, nwritten = 0;
int rdbSaveObject(rio *rdb, robj *o) {
int n = 0, nwritten = 0;
if (o->type == REDIS_STRING) {
/* Save a string value */
@@ -664,8 +664,8 @@ ssize_t 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. */
size_t rdbSavedObjectLen(robj *o) {
ssize_t len = rdbSaveObject(NULL,o);
off_t rdbSavedObjectLen(robj *o) {
int len = rdbSaveObject(NULL,o);
redisAssertWithInfo(NULL,o,len != -1);
return len;
}
+3 -2
View File
@@ -109,8 +109,9 @@ int rdbSaveBackground(char *filename);
int rdbSaveToSlavesSockets(void);
void rdbRemoveTempFile(pid_t childpid);
int rdbSave(char *filename);
ssize_t rdbSaveObject(rio *rdb, robj *o);
size_t rdbSavedObjectLen(robj *o);
int rdbSaveObject(rio *rdb, robj *o);
off_t rdbSavedObjectLen(robj *o);
off_t rdbSavedObjectPages(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 */
size_t written; /* Bytes of 'obuf' already written */
unsigned int 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;
ssize_t nwritten = write(c->context->fd,ptr,sdslen(c->obuf)-c->written);
int 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));
+7 -17
View File
@@ -64,17 +64,6 @@
#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;
@@ -791,9 +780,6 @@ 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;
@@ -1142,6 +1128,13 @@ 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 {
@@ -2207,9 +2200,6 @@ 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
+6 -20
View File
@@ -1496,6 +1496,7 @@ void initServerConfig(void) {
server.repl_disable_tcp_nodelay = REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY;
server.repl_diskless_sync = REDIS_DEFAULT_REPL_DISKLESS_SYNC;
server.repl_diskless_sync_delay = REDIS_DEFAULT_REPL_DISKLESS_SYNC_DELAY;
server.repl_no_sync = REDIS_DEFAULT_REPL_NO_SYNC;
server.slave_priority = REDIS_DEFAULT_SLAVE_PRIORITY;
server.master_repl_offset = 0;
@@ -2736,13 +2737,9 @@ 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
@@ -2754,9 +2751,6 @@ 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,
@@ -2764,33 +2758,25 @@ 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",
"mem_allocator:%s\r\n"
"maxmemory_policy:%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,
memory_lua,
used_memory_lua_hmem,
server.maxmemory,
maxmemory_hmem,
evict_policy,
((long long)lua_gc(server.lua,LUA_GCCOUNT,0))*1024LL,
zmalloc_get_fragmentation_ratio(server.resident_set_size),
ZMALLOC_LIB
ZMALLOC_LIB,
evict_policy
);
}
+7 -4
View File
@@ -119,6 +119,7 @@ typedef long long mstime_t; /* millisecond time type. */
#define REDIS_DEFAULT_RDB_FILENAME "dump.rdb"
#define REDIS_DEFAULT_REPL_DISKLESS_SYNC 0
#define REDIS_DEFAULT_REPL_DISKLESS_SYNC_DELAY 5
#define REDIS_DEFAULT_REPL_NO_SYNC 0
#define REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA 1
#define REDIS_DEFAULT_SLAVE_READ_ONLY 1
#define REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY 0
@@ -258,6 +259,7 @@ typedef long long mstime_t; /* millisecond time type. */
#define REDIS_READONLY (1<<17) /* Cluster client is in read-only state. */
#define REDIS_PUBSUB (1<<18) /* Client is in Pub/Sub mode. */
#define REDIS_PREVENT_PROP (1<<19) /* Don't propagate to AOF / Slaves. */
#define REDIS_SLAVE_NO_SYNC (1<<20) /* Result of: REPLCONF use-sync 0. */
/* Client block type (btype field in client structure)
* if REDIS_BLOCKED flag is set. */
@@ -543,8 +545,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 long reply_bytes; /* Tot bytes of objects in reply list */
size_t sentlen; /* Amount of bytes already sent in the current
unsigned long reply_bytes; /* Tot bytes of objects in reply list */
int 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 +556,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 */
@@ -853,6 +855,7 @@ struct redisServer {
int slave_priority; /* Reported in INFO and used by Sentinel. */
char repl_master_runid[REDIS_RUN_ID_SIZE+1]; /* Master run id for PSYNC. */
long long repl_master_initial_offset; /* Master PSYNC offset. */
int repl_no_sync; /* Streaming replication, no SYNC. */
/* Replication script cache. */
dict *repl_scriptcache_dict; /* SHA1 all slaves are aware of. */
list *repl_scriptcache_fifo; /* First in, first out LRU eviction. */
+53 -2
View File
@@ -480,6 +480,9 @@ void syncCommand(redisClient *c) {
return;
}
/* Accept the slave ASAP if it requested no-sync mode. */
if (c->flags & REDIS_SLAVE_NO_SYNC) goto attach_slave;
redisLog(REDIS_NOTICE,"Slave %s asks for synchronization",
replicationGetSlaveName(c));
@@ -571,6 +574,8 @@ void syncCommand(redisClient *c) {
}
}
attach_slave:
if (server.repl_disable_tcp_nodelay)
anetDisableTcpNoDelay(NULL, c->fd); /* Non critical if it fails. */
c->repldbfd = -1;
@@ -579,6 +584,18 @@ void syncCommand(redisClient *c) {
listAddNodeTail(server.slaves,c);
if (listLength(server.slaves) == 1 && server.repl_backlog == NULL)
createReplicationBacklog();
/* Put the slave fully online right now if it is a no-sync one. */
if (c->flags & REDIS_SLAVE_NO_SYNC) {
char buf[128];
int buflen;
/* Inform the slave with +NOTNEEDED that the request was accepted. */
buflen = snprintf(buf,sizeof(buf),"+NOTNEEDED\r\n");
if (write(c->fd,buf,buflen) != buflen) freeClientAsync(c);
aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
putSlaveOnline(c);
}
return;
}
@@ -613,6 +630,18 @@ void replconfCommand(redisClient *c) {
&port,NULL) != REDIS_OK))
return;
c->slave_listening_port = port;
} else if (!strcasecmp(c->argv[j]->ptr,"use-sync")) {
/* REPLCONF USE-SYNC is used in order for the slave to request
* synchronization-less replication, the master will just send
* new data without trying to synchronize the slave. */
long long val;
if ((getLongLongFromObject(c->argv[j+1], &val) != REDIS_OK))
return;
if (val)
c->flags &= ~REDIS_SLAVE_NO_SYNC;
else
c->flags |= REDIS_SLAVE_NO_SYNC;
} else if (!strcasecmp(c->argv[j]->ptr,"ack")) {
/* REPLCONF ACK is used by slave to inform the master the amount
* of replication stream that it processed so far. It is an
@@ -1140,7 +1169,7 @@ int slaveTryPartialResynchronization(int fd) {
* client structure representing the master into server.master. */
server.repl_master_initial_offset = -1;
if (server.cached_master) {
if (server.cached_master && server.repl_no_sync == 0) {
psync_runid = server.cached_master->replrunid;
snprintf(psync_offset,sizeof(psync_offset),"%lld", server.cached_master->reploff+1);
redisLog(REDIS_NOTICE,"Trying a partial resynchronization (request %s:%s).", psync_runid, psync_offset);
@@ -1186,6 +1215,15 @@ int slaveTryPartialResynchronization(int fd) {
return PSYNC_FULLRESYNC;
}
if (!strncmp(reply,"+NOTNEEDED",10)) {
/* No-sync replication. */
redisLog(REDIS_NOTICE, "No-sync slave accepted without SYNC phase.");
sdsfree(reply);
server.repl_master_initial_offset = 0;
replicationCreateMasterClient(fd);
return PSYNC_CONTINUE;
}
if (!strncmp(reply,"+CONTINUE",9)) {
/* Partial resync was accepted, set the replication state accordingly */
redisLog(REDIS_NOTICE,
@@ -1292,7 +1330,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
}
/* AUTH with the master if required. */
if(server.masterauth) {
if (server.masterauth) {
err = sendSynchronousCommand(fd,"AUTH",server.masterauth,NULL);
if (err[0] == '-') {
redisLog(REDIS_WARNING,"Unable to AUTH to MASTER: %s",err);
@@ -1302,6 +1340,18 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
sdsfree(err);
}
/* Inform the master we don't want to synchronize if we are using
* repl-no-sync mode. */
if (server.repl_no_sync) {
err = sendSynchronousCommand(fd,"REPLCONF","use-sync","0",NULL);
if (err[0] == '-') {
redisLog(REDIS_WARNING,"Unable to enable no-sync mode: %s",err);
sdsfree(err);
goto error;
}
sdsfree(err);
}
/* Set the slave port, so that Master's INFO command can list the
* slave listening port correctly. */
{
@@ -1698,6 +1748,7 @@ void refreshGoodSlavesCount(void) {
time_t lag = server.unixtime - slave->repl_ack_time;
if (slave->replstate == REDIS_REPL_ONLINE &&
!(slave->flags & REDIS_SLAVE_NO_SYNC) &&
lag <= server.repl_min_slaves_max_lag) good++;
}
server.repl_good_slaves_count = good;
+7 -19
View File
@@ -2741,12 +2741,6 @@ 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. */
@@ -2862,30 +2856,24 @@ 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;
}
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 (!strcasecmp(section,"server") || defsections) {
if (sections++) info = sdscat(info,"\r\n");
sds serversection = genRedisInfoString("server");
info = sdscatlen(info,serversection,sdslen(serversection));
sdsfree(serversection);
}
if (defsections || allsections || !strcasecmp(section,"sentinel")) {
if (!strcasecmp(section,"sentinel") || defsections) {
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,sdsnewlen(NULL, offset+sdslen(value)));
o = createObject(REDIS_STRING,sdsempty());
dbAdd(c->db,c->argv[1],o);
} else {
size_t olen;
+1 -1
View File
@@ -1 +1 @@
#define REDIS_VERSION "3.1.999"
#define REDIS_VERSION "2.9.999"
+1 -3
View File
@@ -16,10 +16,8 @@ set ::all_tests {
unit/dump
unit/auth
unit/protocol
unit/keyspace
unit/basic
unit/scan
unit/type/string
unit/type/incr
unit/type/list
unit/type/list-2
unit/type/list-3
@@ -1,4 +1,9 @@
start_server {tags {"string"}} {
start_server {tags {"basic"}} {
test {DEL all keys to start with a clean DB} {
foreach key [r keys *] {r del $key}
r dbsize
} {0}
test {SET and GET an item} {
r set x foobar
r get x
@@ -9,6 +14,38 @@ start_server {tags {"string"}} {
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
@@ -38,7 +75,6 @@ start_server {tags {"string"}} {
} {}
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
@@ -54,11 +90,157 @@ start_server {tags {"string"}} {
set _ $err
} {}
test {DBSIZE should be 10000 now} {
test {DBSIZE should be 10101 now} {
r dbsize
} {10000}
} {10101}
}
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]
@@ -102,6 +284,172 @@ start_server {tags {"string"}} {
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
@@ -119,8 +467,37 @@ start_server {tags {"string"}} {
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}
@@ -415,6 +792,13 @@ start_server {tags {"string"}} {
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
-249
View File
@@ -1,249 +0,0 @@
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
@@ -1,147 +0,0 @@
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}
}
+13 -23
View File
@@ -68,30 +68,26 @@ int sortPointers(const void *a, const void *b) {
return la-lb;
}
void stressGetKeys(dict *d, int times, int *perfect_run, int *approx_run) {
void stressGetKeys(dict *d, int times) {
int j;
dictEntry **des = zmalloc(sizeof(dictEntry*)*dictSize(d));
for (j = 0; j < times; j++) {
int requested = rand() % (dictSize(d)+1);
int returned = dictGetSomeKeys(d, des, requested);
int dup = 0;
int returned = dictGetRandomKeys(d, des, requested);
if (requested != returned) {
printf("*** ERROR! Req: %d, Ret: %d\n", requested, returned);
exit(1);
}
qsort(des,returned,sizeof(dictEntry*),sortPointers);
if (returned > 1) {
int i;
for (i = 0; i < returned-1; i++) {
if (des[i] == des[i+1]) dup++;
if (des[i] == des[i+1]) {
printf("*** ERROR! Duplicated element detected\n");
exit(1);
}
}
}
if (requested == returned && dup == 0) {
(*perfect_run)++;
} else {
(*approx_run)++;
printf("Requested, returned, duplicated: %d %d %d\n",
requested, returned, dup);
}
}
zfree(des);
}
@@ -117,24 +113,18 @@ int main(void) {
dictRelease(d);
d = dictCreate(&dictTypeTest,NULL);
printf("Stress testing dictGetSomeKeys\n");
int perfect_run = 0, approx_run = 0;
printf("Getkeys stress test\n");
for (i = 0; i < MAX2; i++) {
dictAdd(d,(void*)i,NULL);
stressGetKeys(d,100,&perfect_run,&approx_run);
stressGetKeys(d,100);
}
for (i = 0; i < MAX2; i++) {
dictDelete(d,(void*)i);
dictResize(d);
stressGetKeys(d,100,&perfect_run,&approx_run);
stressGetKeys(d,100);
}
printf("dictGetSomeKey, %d perfect runs, %d approximated runs\n",
perfect_run, approx_run);
dictRelease(d);
printf("TEST PASSED!\n");