Commit c1b27012 authored by antirez's avatar antirez
Browse files

Merge remote-tracking branch 'origin/2.2' into 2.2

parents 62e6f6c2 5960ac9d
...@@ -12,6 +12,22 @@ for 2.0. ...@@ -12,6 +12,22 @@ for 2.0.
CHANGELOG CHANGELOG
--------- ---------
What's new in Redis 2.2.12
==========================
* The Slowlog feature was backported to Redis 2.2.
* A number of fixes related blocking operations on lists when mixed with
AOF and Replication.
* Fixed bad interactions between EXPIRE, EXPIREAT, and in general volatile
keys when AOF is enabled. More details in the Redis Google Group here:
http://groups.google.com/group/redis-db/browse_frm/thread/5a931fefb88b16d5?tvc=1
* no more allocation stats info in INFO.
* colorized make for 2.2 as well.
* Fixed a problem with AOF when it is stopped via CONFIG SET appendonly no.
* Warn the user enabling VM that VM is deprecated and discouraged.
* prepareForShutdown() fixed for correctness.
* Close the listening sockets on exit for faster restarts.
What's new in Redis 2.2.11 What's new in Redis 2.2.11
========================== ==========================
......
...@@ -314,10 +314,13 @@ slowlog-log-slower-than 10000 ...@@ -314,10 +314,13 @@ slowlog-log-slower-than 10000
# There is no limit to this length. Just be aware that it will consume memory. # There is no limit to this length. Just be aware that it will consume memory.
# You can reclaim memory used by the slow log with SLOWLOG RESET. # You can reclaim memory used by the slow log with SLOWLOG RESET.
slowlog-log-len 1024 slowlog-max-len 1024
################################ VIRTUAL MEMORY ############################### ################################ VIRTUAL MEMORY ###############################
### WARNING! Virtual Memory is deprecated in Redis 2.4
### The use of Virtual Memory is strongly discouraged.
# Virtual Memory allows Redis to work with datasets bigger than the actual # Virtual Memory allows Redis to work with datasets bigger than the actual
# amount of RAM needed to hold the whole dataset in memory. # amount of RAM needed to hold the whole dataset in memory.
# In order to do so very used keys are taken in memory while the other keys # In order to do so very used keys are taken in memory while the other keys
......
...@@ -5,6 +5,19 @@ ...@@ -5,6 +5,19 @@
release_hdr := $(shell sh -c './mkreleasehdr.sh') release_hdr := $(shell sh -c './mkreleasehdr.sh')
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
OPTIMIZATION?=-O2 OPTIMIZATION?=-O2
CCCOLOR="\033[34m"
LINKCOLOR="\033[34;1m"
SRCCOLOR="\033[33m"
BINCOLOR="\033[37;1m"
MAKECOLOR="\033[32;1m"
ENDCOLOR="\033[0m"
ifndef V
QUIET_CC = @printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR);
QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR);
endif
ifeq ($(uname_S),SunOS) ifeq ($(uname_S),SunOS)
CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -Wall -W -D__EXTENSIONS__ -D_XPG6 CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -Wall -W -D__EXTENSIONS__ -D_XPG6
CCLINK?= -ldl -lnsl -lsocket -lm -lpthread CCLINK?= -ldl -lnsl -lsocket -lm -lpthread
...@@ -111,33 +124,36 @@ zipmap.o: zipmap.c zmalloc.h ...@@ -111,33 +124,36 @@ zipmap.o: zipmap.c zmalloc.h
zmalloc.o: zmalloc.c config.h zmalloc.h zmalloc.o: zmalloc.c config.h zmalloc.h
dependencies: dependencies:
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)hiredis$(ENDCOLOR)
cd ../deps/hiredis && $(MAKE) static ARCH="$(ARCH)" cd ../deps/hiredis && $(MAKE) static ARCH="$(ARCH)"
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)linenoise$(ENDCOLOR)
cd ../deps/linenoise && $(MAKE) ARCH="$(ARCH)" cd ../deps/linenoise && $(MAKE) ARCH="$(ARCH)"
redis-server: $(OBJ) redis-server: $(OBJ)
$(CC) -o $(PRGNAME) $(CCOPT) $(DEBUG) $(OBJ) $(QUIET_LINK)$(CC) -o $(PRGNAME) $(CCOPT) $(DEBUG) $(OBJ)
redis-benchmark: dependencies $(BENCHOBJ) redis-benchmark: dependencies $(BENCHOBJ)
cd ../deps/hiredis && $(MAKE) static @printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)hiredis$(ENDCOLOR)
$(CC) -o $(BENCHPRGNAME) $(CCOPT) $(DEBUG) $(BENCHOBJ) ../deps/hiredis/libhiredis.a cd ../deps/hiredis && $(MAKE) static ARCH="$(ARCH)"
$(QUIET_LINK)$(CC) -o $(BENCHPRGNAME) $(CCOPT) $(DEBUG) $(BENCHOBJ) ../deps/hiredis/libhiredis.a
redis-benchmark.o: redis-benchmark.o:
$(CC) -c $(CFLAGS) -I../deps/hiredis $(DEBUG) $(COMPILE_TIME) $< $(QUIET_CC)$(CC) -c $(CFLAGS) -I../deps/hiredis $(DEBUG) $(COMPILE_TIME) $<
redis-cli: dependencies $(CLIOBJ) redis-cli: dependencies $(CLIOBJ)
$(CC) -o $(CLIPRGNAME) $(CCOPT) $(DEBUG) $(CLIOBJ) ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(QUIET_LINK)$(CC) -o $(CLIPRGNAME) $(CCOPT) $(DEBUG) $(CLIOBJ) ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o
redis-cli.o: redis-cli.o:
$(CC) -c $(CFLAGS) -I../deps/hiredis -I../deps/linenoise $(DEBUG) $(COMPILE_TIME) $< $(QUIET_CC)$(CC) -c $(CFLAGS) -I../deps/hiredis -I../deps/linenoise $(DEBUG) $(COMPILE_TIME) $<
redis-check-dump: $(CHECKDUMPOBJ) redis-check-dump: $(CHECKDUMPOBJ)
$(CC) -o $(CHECKDUMPPRGNAME) $(CCOPT) $(DEBUG) $(CHECKDUMPOBJ) $(QUIET_LINK)$(CC) -o $(CHECKDUMPPRGNAME) $(CCOPT) $(DEBUG) $(CHECKDUMPOBJ)
redis-check-aof: $(CHECKAOFOBJ) redis-check-aof: $(CHECKAOFOBJ)
$(CC) -o $(CHECKAOFPRGNAME) $(CCOPT) $(DEBUG) $(CHECKAOFOBJ) $(QUIET_LINK)$(CC) -o $(CHECKAOFPRGNAME) $(CCOPT) $(DEBUG) $(CHECKAOFOBJ)
.c.o: .c.o:
$(CC) -c $(CFLAGS) $(DEBUG) $(COMPILE_TIME) $< $(QUIET_CC)$(CC) -c $(CFLAGS) $(DEBUG) $(COMPILE_TIME) $<
clean: clean:
rm -rf $(PRGNAME) $(BENCHPRGNAME) $(CLIPRGNAME) $(CHECKDUMPPRGNAME) $(CHECKAOFPRGNAME) *.o *.gcda *.gcno *.gcov rm -rf $(PRGNAME) $(BENCHPRGNAME) $(CLIPRGNAME) $(CHECKDUMPPRGNAME) $(CHECKAOFPRGNAME) *.o *.gcda *.gcno *.gcov
......
...@@ -19,15 +19,15 @@ void stopAppendOnly(void) { ...@@ -19,15 +19,15 @@ void stopAppendOnly(void) {
server.appendseldb = -1; server.appendseldb = -1;
server.appendonly = 0; server.appendonly = 0;
/* rewrite operation in progress? kill it, wait child exit */ /* rewrite operation in progress? kill it, wait child exit */
if (server.bgsavechildpid != -1) { if (server.bgrewritechildpid != -1) {
int statloc; int statloc;
if (kill(server.bgsavechildpid,SIGKILL) != -1) if (kill(server.bgrewritechildpid,SIGKILL) != -1)
wait3(&statloc,0,NULL); wait3(&statloc,0,NULL);
/* reset the buffer accumulating changes while the child saves */ /* reset the buffer accumulating changes while the child saves */
sdsfree(server.bgrewritebuf); sdsfree(server.bgrewritebuf);
server.bgrewritebuf = sdsempty(); server.bgrewritebuf = sdsempty();
server.bgsavechildpid = -1; server.bgrewritechildpid = -1;
} }
} }
......
...@@ -30,6 +30,7 @@ void loadServerConfig(char *filename) { ...@@ -30,6 +30,7 @@ void loadServerConfig(char *filename) {
char buf[REDIS_CONFIGLINE_MAX+1], *err = NULL; char buf[REDIS_CONFIGLINE_MAX+1], *err = NULL;
int linenum = 0; int linenum = 0;
sds line = NULL; sds line = NULL;
int really_use_vm = 0;
if (filename[0] == '-' && filename[1] == '\0') if (filename[0] == '-' && filename[1] == '\0')
fp = stdin; fp = stdin;
...@@ -243,6 +244,10 @@ void loadServerConfig(char *filename) { ...@@ -243,6 +244,10 @@ void loadServerConfig(char *filename) {
if ((server.vm_enabled = yesnotoi(argv[1])) == -1) { if ((server.vm_enabled = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr; err = "argument must be 'yes' or 'no'"; goto loaderr;
} }
} else if (!strcasecmp(argv[0],"really-use-vm") && argc == 2) {
if ((really_use_vm = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"vm-swap-file") && argc == 2) { } else if (!strcasecmp(argv[0],"vm-swap-file") && argc == 2) {
zfree(server.vm_swap_file); zfree(server.vm_swap_file);
server.vm_swap_file = zstrdup(argv[1]); server.vm_swap_file = zstrdup(argv[1]);
...@@ -303,6 +308,7 @@ void loadServerConfig(char *filename) { ...@@ -303,6 +308,7 @@ void loadServerConfig(char *filename) {
sdsfree(line); sdsfree(line);
} }
if (fp != stdin) fclose(fp); if (fp != stdin) fclose(fp);
if (server.vm_enabled && !really_use_vm) goto vm_warning;
return; return;
loaderr: loaderr:
...@@ -311,6 +317,15 @@ loaderr: ...@@ -311,6 +317,15 @@ loaderr:
fprintf(stderr, ">>> '%s'\n", line); fprintf(stderr, ">>> '%s'\n", line);
fprintf(stderr, "%s\n", err); fprintf(stderr, "%s\n", err);
exit(1); exit(1);
vm_warning:
fprintf(stderr, "\nARE YOU SURE YOU WANT TO USE VM?\n\n");
fprintf(stderr, "Redis Virtual Memory is going to be deprecated soon,\n");
fprintf(stderr, "we think you should NOT use it, but use Redis only if\n");
fprintf(stderr, "your data is suitable for an in-memory database.\n");
fprintf(stderr, "If you *really* want VM add this in the config file:\n");
fprintf(stderr, "\n really-use-vm yes\n\n");
exit(1);
} }
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
......
...@@ -455,6 +455,9 @@ int expireIfNeeded(redisDb *db, robj *key) { ...@@ -455,6 +455,9 @@ int expireIfNeeded(redisDb *db, robj *key) {
if (when < 0) return 0; /* No expire for this key */ if (when < 0) return 0; /* No expire for this key */
/* Don't expire anything while loading. It will be done later. */
if (server.loading) return 0;
/* If we are running in the context of a slave, return ASAP: /* If we are running in the context of a slave, return ASAP:
* the slave key expiration is controlled by the master that will * the slave key expiration is controlled by the master that will
* send us synthesized DEL operations for expired keys. * send us synthesized DEL operations for expired keys.
...@@ -492,10 +495,24 @@ void expireGenericCommand(redisClient *c, robj *key, robj *param, long offset) { ...@@ -492,10 +495,24 @@ void expireGenericCommand(redisClient *c, robj *key, robj *param, long offset) {
addReply(c,shared.czero); addReply(c,shared.czero);
return; return;
} }
if (seconds <= 0) { /* EXPIRE with negative TTL, or EXPIREAT with a timestamp into the past
if (dbDelete(c->db,key)) server.dirty++; * should never be executed as a DEL when load the AOF or in the context
addReply(c, shared.cone); * of a slave instance.
*
* Instead we take the other branch of the IF statement setting an expire
* (possibly in the past) and wait for an explicit DEL from the master. */
if (seconds <= 0 && !server.loading && !server.masterhost) {
robj *aux;
redisAssert(dbDelete(c->db,key));
server.dirty++;
/* Replicate/AOF this as an explicit DEL. */
aux = createStringObject("DEL",3);
rewriteClientCommandVector(c,2,aux,key);
decrRefCount(aux);
touchWatchedKey(c->db,key); touchWatchedKey(c->db,key);
addReply(c, shared.cone);
return; return;
} else { } else {
time_t when = time(NULL)+seconds; time_t when = time(NULL)+seconds;
......
...@@ -24,14 +24,14 @@ void freeClientMultiState(redisClient *c) { ...@@ -24,14 +24,14 @@ void freeClientMultiState(redisClient *c) {
} }
/* Add a new command into the MULTI commands queue */ /* Add a new command into the MULTI commands queue */
void queueMultiCommand(redisClient *c, struct redisCommand *cmd) { void queueMultiCommand(redisClient *c) {
multiCmd *mc; multiCmd *mc;
int j; int j;
c->mstate.commands = zrealloc(c->mstate.commands, c->mstate.commands = zrealloc(c->mstate.commands,
sizeof(multiCmd)*(c->mstate.count+1)); sizeof(multiCmd)*(c->mstate.count+1));
mc = c->mstate.commands+c->mstate.count; mc = c->mstate.commands+c->mstate.count;
mc->cmd = cmd; mc->cmd = c->cmd;
mc->argc = c->argc; mc->argc = c->argc;
mc->argv = zmalloc(sizeof(robj*)*c->argc); mc->argv = zmalloc(sizeof(robj*)*c->argc);
memcpy(mc->argv,c->argv,sizeof(robj*)*c->argc); memcpy(mc->argv,c->argv,sizeof(robj*)*c->argc);
...@@ -78,6 +78,7 @@ void execCommand(redisClient *c) { ...@@ -78,6 +78,7 @@ void execCommand(redisClient *c) {
int j; int j;
robj **orig_argv; robj **orig_argv;
int orig_argc; int orig_argc;
struct redisCommand *orig_cmd;
if (!(c->flags & REDIS_MULTI)) { if (!(c->flags & REDIS_MULTI)) {
addReplyError(c,"EXEC without MULTI"); addReplyError(c,"EXEC without MULTI");
...@@ -105,18 +106,22 @@ void execCommand(redisClient *c) { ...@@ -105,18 +106,22 @@ void execCommand(redisClient *c) {
unwatchAllKeys(c); /* Unwatch ASAP otherwise we'll waste CPU cycles */ unwatchAllKeys(c); /* Unwatch ASAP otherwise we'll waste CPU cycles */
orig_argv = c->argv; orig_argv = c->argv;
orig_argc = c->argc; orig_argc = c->argc;
orig_cmd = c->cmd;
addReplyMultiBulkLen(c,c->mstate.count); addReplyMultiBulkLen(c,c->mstate.count);
for (j = 0; j < c->mstate.count; j++) { for (j = 0; j < c->mstate.count; j++) {
c->argc = c->mstate.commands[j].argc; c->argc = c->mstate.commands[j].argc;
c->argv = c->mstate.commands[j].argv; c->argv = c->mstate.commands[j].argv;
call(c,c->mstate.commands[j].cmd); c->cmd = c->mstate.commands[j].cmd;
call(c);
/* Commands may alter argc/argv, restore mstate. */ /* Commands may alter argc/argv, restore mstate. */
c->mstate.commands[j].argc = c->argc; c->mstate.commands[j].argc = c->argc;
c->mstate.commands[j].argv = c->argv; c->mstate.commands[j].argv = c->argv;
c->mstate.commands[j].cmd = c->cmd;
} }
c->argv = orig_argv; c->argv = orig_argv;
c->argc = orig_argc; c->argc = orig_argc;
c->cmd = orig_cmd;
freeClientMultiState(c); freeClientMultiState(c);
initClientMultiState(c); initClientMultiState(c);
c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS); c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS);
......
...@@ -31,6 +31,7 @@ redisClient *createClient(int fd) { ...@@ -31,6 +31,7 @@ redisClient *createClient(int fd) {
c->reqtype = 0; c->reqtype = 0;
c->argc = 0; c->argc = 0;
c->argv = NULL; c->argv = NULL;
c->cmd = NULL;
c->multibulklen = 0; c->multibulklen = 0;
c->bulklen = -1; c->bulklen = -1;
c->sentlen = 0; c->sentlen = 0;
...@@ -444,6 +445,7 @@ static void freeClientArgv(redisClient *c) { ...@@ -444,6 +445,7 @@ static void freeClientArgv(redisClient *c) {
for (j = 0; j < c->argc; j++) for (j = 0; j < c->argc; j++)
decrRefCount(c->argv[j]); decrRefCount(c->argv[j]);
c->argc = 0; c->argc = 0;
c->cmd = NULL;
} }
void freeClient(redisClient *c) { void freeClient(redisClient *c) {
...@@ -896,4 +898,3 @@ void rewriteClientCommandVector(redisClient *c, int argc, ...) { ...@@ -896,4 +898,3 @@ void rewriteClientCommandVector(redisClient *c, int argc, ...) {
c->argc = argc; c->argc = argc;
va_end(ap); va_end(ap);
} }
...@@ -677,7 +677,7 @@ void beforeSleep(struct aeEventLoop *eventLoop) { ...@@ -677,7 +677,7 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
readQueryFromClient, c); readQueryFromClient, c);
cmd = lookupCommand(c->argv[0]->ptr); cmd = lookupCommand(c->argv[0]->ptr);
redisAssert(cmd != NULL); redisAssert(cmd != NULL);
call(c,cmd); call(c);
resetClient(c); resetClient(c);
/* There may be more data to process in the input buffer. */ /* There may be more data to process in the input buffer. */
if (c->querybuf && sdslen(c->querybuf) > 0) if (c->querybuf && sdslen(c->querybuf) > 0)
...@@ -958,18 +958,18 @@ struct redisCommand *lookupCommandByCString(char *s) { ...@@ -958,18 +958,18 @@ struct redisCommand *lookupCommandByCString(char *s) {
} }
/* Call() is the core of Redis execution of a command */ /* Call() is the core of Redis execution of a command */
void call(redisClient *c, struct redisCommand *cmd) { void call(redisClient *c) {
long long dirty, start = ustime(), duration; long long dirty, start = ustime(), duration;
dirty = server.dirty; dirty = server.dirty;
cmd->proc(c); c->cmd->proc(c);
dirty = server.dirty-dirty; dirty = server.dirty-dirty;
duration = ustime()-start; duration = ustime()-start;
slowlogPushEntryIfNeeded(c->argv,c->argc,duration); slowlogPushEntryIfNeeded(c->argv,c->argc,duration);
if (server.appendonly && dirty) if (server.appendonly && dirty)
feedAppendOnlyFile(cmd,c->db->id,c->argv,c->argc); feedAppendOnlyFile(c->cmd,c->db->id,c->argv,c->argc);
if ((dirty || cmd->flags & REDIS_CMD_FORCE_REPLICATION) && if ((dirty || c->cmd->flags & REDIS_CMD_FORCE_REPLICATION) &&
listLength(server.slaves)) listLength(server.slaves))
replicationFeedSlaves(server.slaves,c->db->id,c->argv,c->argc); replicationFeedSlaves(server.slaves,c->db->id,c->argv,c->argc);
if (listLength(server.monitors)) if (listLength(server.monitors))
...@@ -986,8 +986,6 @@ void call(redisClient *c, struct redisCommand *cmd) { ...@@ -986,8 +986,6 @@ void call(redisClient *c, struct redisCommand *cmd) {
* and other operations can be performed by the caller. Otherwise * and other operations can be performed by the caller. Otherwise
* if 0 is returned the client was destroied (i.e. after QUIT). */ * if 0 is returned the client was destroied (i.e. after QUIT). */
int processCommand(redisClient *c) { int processCommand(redisClient *c) {
struct redisCommand *cmd;
/* The QUIT command is handled separately. Normal command procs will /* The QUIT command is handled separately. Normal command procs will
* go through checking for replication and QUIT will cause trouble * go through checking for replication and QUIT will cause trouble
* when FORCE_REPLICATION is enabled and would be implemented in * when FORCE_REPLICATION is enabled and would be implemented in
...@@ -999,21 +997,22 @@ int processCommand(redisClient *c) { ...@@ -999,21 +997,22 @@ int processCommand(redisClient *c) {
} }
/* Now lookup the command and check ASAP about trivial error conditions /* Now lookup the command and check ASAP about trivial error conditions
* such wrong arity, bad command name and so forth. */ * such as wrong arity, bad command name and so forth. */
cmd = lookupCommand(c->argv[0]->ptr); c->cmd = lookupCommand(c->argv[0]->ptr);
if (!cmd) { if (!c->cmd) {
addReplyErrorFormat(c,"unknown command '%s'", addReplyErrorFormat(c,"unknown command '%s'",
(char*)c->argv[0]->ptr); (char*)c->argv[0]->ptr);
return REDIS_OK; return REDIS_OK;
} else if ((cmd->arity > 0 && cmd->arity != c->argc) || } else if ((c->cmd->arity > 0 && c->cmd->arity != c->argc) ||
(c->argc < -cmd->arity)) { (c->argc < -c->cmd->arity)) {
addReplyErrorFormat(c,"wrong number of arguments for '%s' command", addReplyErrorFormat(c,"wrong number of arguments for '%s' command",
cmd->name); c->cmd->name);
return REDIS_OK; return REDIS_OK;
} }
/* Check if the user is authenticated */ /* Check if the user is authenticated */
if (server.requirepass && !c->authenticated && cmd->proc != authCommand) { if (server.requirepass && !c->authenticated && c->cmd->proc != authCommand)
{
addReplyError(c,"operation not permitted"); addReplyError(c,"operation not permitted");
return REDIS_OK; return REDIS_OK;
} }
...@@ -1024,7 +1023,7 @@ int processCommand(redisClient *c) { ...@@ -1024,7 +1023,7 @@ int processCommand(redisClient *c) {
* keys in the dataset). If there are not the only thing we can do * keys in the dataset). If there are not the only thing we can do
* is returning an error. */ * is returning an error. */
if (server.maxmemory) freeMemoryIfNeeded(); if (server.maxmemory) freeMemoryIfNeeded();
if (server.maxmemory && (cmd->flags & REDIS_CMD_DENYOOM) && if (server.maxmemory && (c->cmd->flags & REDIS_CMD_DENYOOM) &&
zmalloc_used_memory() > server.maxmemory) zmalloc_used_memory() > server.maxmemory)
{ {
addReplyError(c,"command not allowed when used memory > 'maxmemory'"); addReplyError(c,"command not allowed when used memory > 'maxmemory'");
...@@ -1034,8 +1033,10 @@ int processCommand(redisClient *c) { ...@@ -1034,8 +1033,10 @@ int processCommand(redisClient *c) {
/* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */ /* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */
if ((dictSize(c->pubsub_channels) > 0 || listLength(c->pubsub_patterns) > 0) if ((dictSize(c->pubsub_channels) > 0 || listLength(c->pubsub_patterns) > 0)
&& &&
cmd->proc != subscribeCommand && cmd->proc != unsubscribeCommand && c->cmd->proc != subscribeCommand &&
cmd->proc != psubscribeCommand && cmd->proc != punsubscribeCommand) { c->cmd->proc != unsubscribeCommand &&
c->cmd->proc != psubscribeCommand &&
c->cmd->proc != punsubscribeCommand) {
addReplyError(c,"only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context"); addReplyError(c,"only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context");
return REDIS_OK; return REDIS_OK;
} }
...@@ -1044,7 +1045,7 @@ int processCommand(redisClient *c) { ...@@ -1044,7 +1045,7 @@ int processCommand(redisClient *c) {
* we are a slave with a broken link with master. */ * we are a slave with a broken link with master. */
if (server.masterhost && server.replstate != REDIS_REPL_CONNECTED && if (server.masterhost && server.replstate != REDIS_REPL_CONNECTED &&
server.repl_serve_stale_data == 0 && server.repl_serve_stale_data == 0 &&
cmd->proc != infoCommand && cmd->proc != slaveofCommand) c->cmd->proc != infoCommand && c->cmd->proc != slaveofCommand)
{ {
addReplyError(c, addReplyError(c,
"link with MASTER is down and slave-serve-stale-data is set to no"); "link with MASTER is down and slave-serve-stale-data is set to no");
...@@ -1052,22 +1053,22 @@ int processCommand(redisClient *c) { ...@@ -1052,22 +1053,22 @@ int processCommand(redisClient *c) {
} }
/* Loading DB? Return an error if the command is not INFO */ /* Loading DB? Return an error if the command is not INFO */
if (server.loading && cmd->proc != infoCommand) { if (server.loading && c->cmd->proc != infoCommand) {
addReply(c, shared.loadingerr); addReply(c, shared.loadingerr);
return REDIS_OK; return REDIS_OK;
} }
/* Exec the command */ /* Exec the command */
if (c->flags & REDIS_MULTI && if (c->flags & REDIS_MULTI &&
cmd->proc != execCommand && cmd->proc != discardCommand && c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
cmd->proc != multiCommand && cmd->proc != watchCommand) c->cmd->proc != multiCommand && c->cmd->proc != watchCommand)
{ {
queueMultiCommand(c,cmd); queueMultiCommand(c);
addReply(c,shared.queued); addReply(c,shared.queued);
} else { } else {
if (server.vm_enabled && server.vm_max_threads > 0 && if (server.vm_enabled && server.vm_max_threads > 0 &&
blockClientOnSwappedKeys(c,cmd)) return REDIS_ERR; blockClientOnSwappedKeys(c)) return REDIS_ERR;
call(c,cmd); call(c);
} }
return REDIS_OK; return REDIS_OK;
} }
...@@ -1075,20 +1076,29 @@ int processCommand(redisClient *c) { ...@@ -1075,20 +1076,29 @@ int processCommand(redisClient *c) {
/*================================== Shutdown =============================== */ /*================================== Shutdown =============================== */
int prepareForShutdown() { int prepareForShutdown() {
redisLog(REDIS_WARNING,"User requested shutdown, saving DB..."); redisLog(REDIS_WARNING,"User requested shutdown...");
/* Kill the saving child if there is a background saving in progress. /* Kill the saving child if there is a background saving in progress.
We want to avoid race conditions, for instance our saving child may We want to avoid race conditions, for instance our saving child may
overwrite the synchronous saving did by SHUTDOWN. */ overwrite the synchronous saving did by SHUTDOWN. */
if (server.bgsavechildpid != -1) { if (server.bgsavechildpid != -1) {
redisLog(REDIS_WARNING,"There is a live saving child. Killing it!"); redisLog(REDIS_WARNING,"There is a child saving an .rdb. Killing it!");
kill(server.bgsavechildpid,SIGKILL); kill(server.bgsavechildpid,SIGKILL);
rdbRemoveTempFile(server.bgsavechildpid); rdbRemoveTempFile(server.bgsavechildpid);
} }
if (server.appendonly) { if (server.appendonly) {
/* Kill the AOF saving child as the AOF we already have may be longer
* but contains the full dataset anyway. */
if (server.bgrewritechildpid != -1) {
redisLog(REDIS_WARNING,
"There is a child rewriting the AOF. Killing it!");
kill(server.bgrewritechildpid,SIGKILL);
}
/* Append only file: fsync() the AOF and exit */ /* Append only file: fsync() the AOF and exit */
redisLog(REDIS_NOTICE,"Calling fsync() on the AOF file.");
aof_fsync(server.appendfd); aof_fsync(server.appendfd);
if (server.vm_enabled) unlink(server.vm_swap_file); }
} else if (server.saveparamslen > 0) { if (server.saveparamslen > 0) {
redisLog(REDIS_NOTICE,"Saving the final RDB snapshot before exiting.");
/* Snapshotting. Perform a SYNC SAVE and exit */ /* Snapshotting. Perform a SYNC SAVE and exit */
if (rdbSave(server.dbfilename) != REDIS_OK) { if (rdbSave(server.dbfilename) != REDIS_OK) {
/* Ooops.. error saving! The best we can do is to continue /* Ooops.. error saving! The best we can do is to continue
...@@ -1096,14 +1106,23 @@ int prepareForShutdown() { ...@@ -1096,14 +1106,23 @@ int prepareForShutdown() {
* in the next cron() Redis will be notified that the background * in the next cron() Redis will be notified that the background
* saving aborted, handling special stuff like slaves pending for * saving aborted, handling special stuff like slaves pending for
* synchronization... */ * synchronization... */
redisLog(REDIS_WARNING,"Error trying to save the DB, can't exit"); redisLog(REDIS_WARNING,"Error trying to save the DB, can't exit.");
return REDIS_ERR; return REDIS_ERR;
} }
} else {
redisLog(REDIS_WARNING,"Not saving DB.");
} }
if (server.daemonize) unlink(server.pidfile); if (server.vm_enabled) {
redisLog(REDIS_WARNING,"Server exit now, bye bye..."); redisLog(REDIS_NOTICE,"Removing the swap file.");
unlink(server.vm_swap_file);
}
if (server.daemonize) {
redisLog(REDIS_NOTICE,"Removing the pid file.");
unlink(server.pidfile);
}
/* Close the listening sockets. Apparently this allows faster restarts. */
if (server.ipfd != -1) close(server.ipfd);
if (server.sofd != -1) close(server.sofd);
redisLog(REDIS_WARNING,"Redis is now ready to exit, bye bye...");
return REDIS_OK; return REDIS_OK;
} }
...@@ -1176,8 +1195,8 @@ sds genRedisInfoString(void) { ...@@ -1176,8 +1195,8 @@ sds genRedisInfoString(void) {
"lru_clock:%ld\r\n" "lru_clock:%ld\r\n"
"used_cpu_sys:%.2f\r\n" "used_cpu_sys:%.2f\r\n"
"used_cpu_user:%.2f\r\n" "used_cpu_user:%.2f\r\n"
"used_cpu_sys_childrens:%.2f\r\n" "used_cpu_sys_children:%.2f\r\n"
"used_cpu_user_childrens:%.2f\r\n" "used_cpu_user_children:%.2f\r\n"
"connected_clients:%d\r\n" "connected_clients:%d\r\n"
"connected_slaves:%d\r\n" "connected_slaves:%d\r\n"
"client_longest_output_list:%lu\r\n" "client_longest_output_list:%lu\r\n"
...@@ -1335,18 +1354,6 @@ sds genRedisInfoString(void) { ...@@ -1335,18 +1354,6 @@ sds genRedisInfoString(void) {
); );
} }
info = sdscat(info,"allocation_stats:");
for (j = 0; j <= ZMALLOC_MAX_ALLOC_STAT; j++) {
size_t count = zmalloc_allocations_for_size(j);
if (count) {
if (info[sdslen(info)-1] != ':') info = sdscatlen(info,",",1);
info = sdscatprintf(info,"%s%d=%zu",
(j == ZMALLOC_MAX_ALLOC_STAT) ? ">=" : "",
j,count);
}
}
info = sdscat(info,"\r\n");
for (j = 0; j < server.dbnum; j++) { for (j = 0; j < server.dbnum; j++) {
long long keys, vkeys; long long keys, vkeys;
......
...@@ -314,6 +314,7 @@ typedef struct redisClient { ...@@ -314,6 +314,7 @@ typedef struct redisClient {
sds querybuf; sds querybuf;
int argc; int argc;
robj **argv; robj **argv;
struct redisCommand *cmd;
int reqtype; int reqtype;
int multibulklen; /* number of multi bulk arguments left to read */ int multibulklen; /* number of multi bulk arguments left to read */
long bulklen; /* length of bulk argument in multi bulk request */ long bulklen; /* length of bulk argument in multi bulk request */
...@@ -700,7 +701,7 @@ void popGenericCommand(redisClient *c, int where); ...@@ -700,7 +701,7 @@ void popGenericCommand(redisClient *c, int where);
void unwatchAllKeys(redisClient *c); void unwatchAllKeys(redisClient *c);
void initClientMultiState(redisClient *c); void initClientMultiState(redisClient *c);
void freeClientMultiState(redisClient *c); void freeClientMultiState(redisClient *c);
void queueMultiCommand(redisClient *c, struct redisCommand *cmd); void queueMultiCommand(redisClient *c);
void touchWatchedKey(redisDb *db, robj *key); void touchWatchedKey(redisDb *db, robj *key);
void touchWatchedKeysOnFlush(int dbid); void touchWatchedKeysOnFlush(int dbid);
...@@ -788,7 +789,7 @@ int processCommand(redisClient *c); ...@@ -788,7 +789,7 @@ int processCommand(redisClient *c);
void setupSignalHandlers(void); void setupSignalHandlers(void);
struct redisCommand *lookupCommand(sds name); struct redisCommand *lookupCommand(sds name);
struct redisCommand *lookupCommandByCString(char *s); struct redisCommand *lookupCommandByCString(char *s);
void call(redisClient *c, struct redisCommand *cmd); void call(redisClient *c);
int prepareForShutdown(); int prepareForShutdown();
void redisLog(int level, const char *fmt, ...); void redisLog(int level, const char *fmt, ...);
void usage(); void usage();
...@@ -819,7 +820,7 @@ void vmReopenSwapFile(void); ...@@ -819,7 +820,7 @@ void vmReopenSwapFile(void);
int vmFreePage(off_t page); int vmFreePage(off_t page);
void zunionInterBlockClientOnSwappedKeys(redisClient *c, struct redisCommand *cmd, int argc, robj **argv); void zunionInterBlockClientOnSwappedKeys(redisClient *c, struct redisCommand *cmd, int argc, robj **argv);
void execBlockClientOnSwappedKeys(redisClient *c, struct redisCommand *cmd, int argc, robj **argv); void execBlockClientOnSwappedKeys(redisClient *c, struct redisCommand *cmd, int argc, robj **argv);
int blockClientOnSwappedKeys(redisClient *c, struct redisCommand *cmd); int blockClientOnSwappedKeys(redisClient *c);
int dontWaitForSwappedKey(redisClient *c, robj *key); int dontWaitForSwappedKey(redisClient *c, robj *key);
void handleClientsBlockedOnSwappedKey(redisDb *db, robj *key); void handleClientsBlockedOnSwappedKey(redisDb *db, robj *key);
vmpointer *vmSwapObjectBlocking(robj *val); vmpointer *vmSwapObjectBlocking(robj *val);
......
...@@ -904,6 +904,7 @@ void blockingPopGenericCommand(redisClient *c, int where) { ...@@ -904,6 +904,7 @@ void blockingPopGenericCommand(redisClient *c, int where) {
if (listTypeLength(o) != 0) { if (listTypeLength(o) != 0) {
/* If the list contains elements fall back to the usual /* If the list contains elements fall back to the usual
* non-blocking POP operation */ * non-blocking POP operation */
struct redisCommand *orig_cmd;
robj *argv[2], **orig_argv; robj *argv[2], **orig_argv;
int orig_argc; int orig_argc;
...@@ -911,6 +912,7 @@ void blockingPopGenericCommand(redisClient *c, int where) { ...@@ -911,6 +912,7 @@ void blockingPopGenericCommand(redisClient *c, int where) {
* popGenericCommand() as the command takes a single key. */ * popGenericCommand() as the command takes a single key. */
orig_argv = c->argv; orig_argv = c->argv;
orig_argc = c->argc; orig_argc = c->argc;
orig_cmd = c->cmd;
argv[1] = c->argv[j]; argv[1] = c->argv[j];
c->argv = argv; c->argv = argv;
c->argc = 2; c->argc = 2;
...@@ -928,6 +930,7 @@ void blockingPopGenericCommand(redisClient *c, int where) { ...@@ -928,6 +930,7 @@ void blockingPopGenericCommand(redisClient *c, int where) {
/* Fix the client structure with the original stuff */ /* Fix the client structure with the original stuff */
c->argv = orig_argv; c->argv = orig_argv;
c->argc = orig_argc; c->argc = orig_argc;
c->cmd = orig_cmd;
return; return;
} }
......
#define REDIS_VERSION "2.2.11" #define REDIS_VERSION "2.2.12"
...@@ -1064,11 +1064,11 @@ void execBlockClientOnSwappedKeys(redisClient *c, struct redisCommand *cmd, int ...@@ -1064,11 +1064,11 @@ void execBlockClientOnSwappedKeys(redisClient *c, struct redisCommand *cmd, int
* *
* Return 1 if the client is marked as blocked, 0 if the client can * Return 1 if the client is marked as blocked, 0 if the client can
* continue as the keys it is going to access appear to be in memory. */ * continue as the keys it is going to access appear to be in memory. */
int blockClientOnSwappedKeys(redisClient *c, struct redisCommand *cmd) { int blockClientOnSwappedKeys(redisClient *c) {
if (cmd->vm_preload_proc != NULL) { if (c->cmd->vm_preload_proc != NULL) {
cmd->vm_preload_proc(c,cmd,c->argc,c->argv); c->cmd->vm_preload_proc(c,c->cmd,c->argc,c->argv);
} else { } else {
waitForMultipleSwappedKeys(c,cmd,c->argc,c->argv); waitForMultipleSwappedKeys(c,c->cmd,c->argc,c->argv);
} }
/* If the client was blocked for at least one key, mark it as blocked. */ /* If the client was blocked for at least one key, mark it as blocked. */
......
...@@ -55,16 +55,13 @@ ...@@ -55,16 +55,13 @@
#define update_zmalloc_stat_alloc(__n,__size) do { \ #define update_zmalloc_stat_alloc(__n,__size) do { \
size_t _n = (__n); \ size_t _n = (__n); \
size_t _stat_slot = (__size < ZMALLOC_MAX_ALLOC_STAT) ? __size : ZMALLOC_MAX_ALLOC_STAT; \
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
if (zmalloc_thread_safe) { \ if (zmalloc_thread_safe) { \
pthread_mutex_lock(&used_memory_mutex); \ pthread_mutex_lock(&used_memory_mutex); \
used_memory += _n; \ used_memory += _n; \
zmalloc_allocations[_stat_slot]++; \
pthread_mutex_unlock(&used_memory_mutex); \ pthread_mutex_unlock(&used_memory_mutex); \
} else { \ } else { \
used_memory += _n; \ used_memory += _n; \
zmalloc_allocations[_stat_slot]++; \
} \ } \
} while(0) } while(0)
...@@ -83,8 +80,6 @@ ...@@ -83,8 +80,6 @@
static size_t used_memory = 0; static size_t used_memory = 0;
static int zmalloc_thread_safe = 0; static int zmalloc_thread_safe = 0;
pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;
/* Note that malloc_allocations elements are initialized to zero by C */
size_t zmalloc_allocations[ZMALLOC_MAX_ALLOC_STAT+1];
static void zmalloc_oom(size_t size) { static void zmalloc_oom(size_t size) {
fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n", fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n",
...@@ -185,11 +180,6 @@ size_t zmalloc_used_memory(void) { ...@@ -185,11 +180,6 @@ size_t zmalloc_used_memory(void) {
return um; return um;
} }
size_t zmalloc_allocations_for_size(size_t size) {
if (size > ZMALLOC_MAX_ALLOC_STAT) return 0;
return zmalloc_allocations[size];
}
void zmalloc_enable_thread_safeness(void) { void zmalloc_enable_thread_safeness(void) {
zmalloc_thread_safe = 1; zmalloc_thread_safe = 1;
} }
......
...@@ -40,8 +40,5 @@ size_t zmalloc_used_memory(void); ...@@ -40,8 +40,5 @@ size_t zmalloc_used_memory(void);
void zmalloc_enable_thread_safeness(void); void zmalloc_enable_thread_safeness(void);
float zmalloc_get_fragmentation_ratio(void); float zmalloc_get_fragmentation_ratio(void);
size_t zmalloc_get_rss(void); size_t zmalloc_get_rss(void);
size_t zmalloc_allocations_for_size(size_t size);
#define ZMALLOC_MAX_ALLOC_STAT 256
#endif /* _ZMALLOC_H */ #endif /* _ZMALLOC_H */
...@@ -101,4 +101,22 @@ tags {"aof"} { ...@@ -101,4 +101,22 @@ tags {"aof"} {
assert_equal 1 [$client scard set] assert_equal 1 [$client scard set]
} }
} }
## Test that EXPIREAT is loaded correctly
create_aof {
append_to_aof [formatCommand rpush list foo]
append_to_aof [formatCommand expireat list 1000]
append_to_aof [formatCommand rpush list bar]
}
start_server_aof [list dir $server_path] {
test "AOF+EXPIRE: Server should have been started" {
assert_equal 1 [is_alive $srv]
}
test "AOF+EXPIRE: List should be empty" {
set client [redis [dict get $srv host] [dict get $srv port]]
assert_equal 0 [$client llen list]
}
}
} }
...@@ -128,7 +128,7 @@ proc execute_everything {} { ...@@ -128,7 +128,7 @@ proc execute_everything {} {
execute_tests "unit/slowlog" execute_tests "unit/slowlog"
# run tests with VM enabled # run tests with VM enabled
set ::global_overrides {vm-enabled yes} set ::global_overrides {vm-enabled yes really-use-vm yes}
execute_tests "unit/protocol" execute_tests "unit/protocol"
execute_tests "unit/basic" execute_tests "unit/basic"
execute_tests "unit/type/list" execute_tests "unit/type/list"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment