diff --git a/00-RELEASENOTES b/00-RELEASENOTES index 7234e29f913e19d66c5bcf5b7c71d858877b622a..e7f91056a09d79b47fe489e6090a5c07a4535e90 100644 --- a/00-RELEASENOTES +++ b/00-RELEASENOTES @@ -12,6 +12,22 @@ for 2.0. 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 ========================== diff --git a/redis.conf b/redis.conf index a545b78b7b6737bc968c5f368f5c36f3cd03c357..a8188a92ec5301825bd568cdbb407c1348f5aaab 100644 --- a/redis.conf +++ b/redis.conf @@ -314,10 +314,13 @@ slowlog-log-slower-than 10000 # 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. -slowlog-log-len 1024 +slowlog-max-len 1024 ################################ 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 # 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 diff --git a/src/Makefile b/src/Makefile index c08d50c32481fedef457dbdc1bf636d837338732..67b09723b5bb1cf8b0d1185354e614d029280daa 100644 --- a/src/Makefile +++ b/src/Makefile @@ -5,6 +5,19 @@ release_hdr := $(shell sh -c './mkreleasehdr.sh') uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') 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) CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -Wall -W -D__EXTENSIONS__ -D_XPG6 CCLINK?= -ldl -lnsl -lsocket -lm -lpthread @@ -111,33 +124,36 @@ zipmap.o: zipmap.c zmalloc.h zmalloc.o: zmalloc.c config.h zmalloc.h dependencies: + @printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)hiredis$(ENDCOLOR) cd ../deps/hiredis && $(MAKE) static ARCH="$(ARCH)" + @printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)linenoise$(ENDCOLOR) cd ../deps/linenoise && $(MAKE) ARCH="$(ARCH)" redis-server: $(OBJ) - $(CC) -o $(PRGNAME) $(CCOPT) $(DEBUG) $(OBJ) + $(QUIET_LINK)$(CC) -o $(PRGNAME) $(CCOPT) $(DEBUG) $(OBJ) redis-benchmark: dependencies $(BENCHOBJ) - cd ../deps/hiredis && $(MAKE) static - $(CC) -o $(BENCHPRGNAME) $(CCOPT) $(DEBUG) $(BENCHOBJ) ../deps/hiredis/libhiredis.a + @printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)hiredis$(ENDCOLOR) + cd ../deps/hiredis && $(MAKE) static ARCH="$(ARCH)" + $(QUIET_LINK)$(CC) -o $(BENCHPRGNAME) $(CCOPT) $(DEBUG) $(BENCHOBJ) ../deps/hiredis/libhiredis.a 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) - $(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: - $(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) - $(CC) -o $(CHECKDUMPPRGNAME) $(CCOPT) $(DEBUG) $(CHECKDUMPOBJ) + $(QUIET_LINK)$(CC) -o $(CHECKDUMPPRGNAME) $(CCOPT) $(DEBUG) $(CHECKDUMPOBJ) redis-check-aof: $(CHECKAOFOBJ) - $(CC) -o $(CHECKAOFPRGNAME) $(CCOPT) $(DEBUG) $(CHECKAOFOBJ) + $(QUIET_LINK)$(CC) -o $(CHECKAOFPRGNAME) $(CCOPT) $(DEBUG) $(CHECKAOFOBJ) .c.o: - $(CC) -c $(CFLAGS) $(DEBUG) $(COMPILE_TIME) $< + $(QUIET_CC)$(CC) -c $(CFLAGS) $(DEBUG) $(COMPILE_TIME) $< clean: rm -rf $(PRGNAME) $(BENCHPRGNAME) $(CLIPRGNAME) $(CHECKDUMPPRGNAME) $(CHECKAOFPRGNAME) *.o *.gcda *.gcno *.gcov diff --git a/src/aof.c b/src/aof.c index 06c9a47d5e1f20731d8d4ec3cccb5f299cafa8ba..b0852c47f8b31c04a2b98fbcb55b9c24d6301217 100644 --- a/src/aof.c +++ b/src/aof.c @@ -19,15 +19,15 @@ void stopAppendOnly(void) { server.appendseldb = -1; server.appendonly = 0; /* rewrite operation in progress? kill it, wait child exit */ - if (server.bgsavechildpid != -1) { + if (server.bgrewritechildpid != -1) { int statloc; - if (kill(server.bgsavechildpid,SIGKILL) != -1) + if (kill(server.bgrewritechildpid,SIGKILL) != -1) wait3(&statloc,0,NULL); /* reset the buffer accumulating changes while the child saves */ sdsfree(server.bgrewritebuf); server.bgrewritebuf = sdsempty(); - server.bgsavechildpid = -1; + server.bgrewritechildpid = -1; } } diff --git a/src/config.c b/src/config.c index f2b0d312a69cb6696ec639f4e3a6059f39b06c53..58d1190f35751f4528527a3300b2985bf53b7a4e 100644 --- a/src/config.c +++ b/src/config.c @@ -30,6 +30,7 @@ void loadServerConfig(char *filename) { char buf[REDIS_CONFIGLINE_MAX+1], *err = NULL; int linenum = 0; sds line = NULL; + int really_use_vm = 0; if (filename[0] == '-' && filename[1] == '\0') fp = stdin; @@ -243,6 +244,10 @@ void loadServerConfig(char *filename) { if ((server.vm_enabled = yesnotoi(argv[1])) == -1) { 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) { zfree(server.vm_swap_file); server.vm_swap_file = zstrdup(argv[1]); @@ -303,6 +308,7 @@ void loadServerConfig(char *filename) { sdsfree(line); } if (fp != stdin) fclose(fp); + if (server.vm_enabled && !really_use_vm) goto vm_warning; return; loaderr: @@ -311,6 +317,15 @@ loaderr: fprintf(stderr, ">>> '%s'\n", line); fprintf(stderr, "%s\n", err); 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); } /*----------------------------------------------------------------------------- diff --git a/src/db.c b/src/db.c index ede242dbe0f1d00a5245baeb8666c2fb0a4e1da3..e38466d3b90ad1c77a9b448215ebfbce5210a6b5 100644 --- a/src/db.c +++ b/src/db.c @@ -455,6 +455,9 @@ int expireIfNeeded(redisDb *db, robj *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: * the slave key expiration is controlled by the master that will * send us synthesized DEL operations for expired keys. @@ -492,10 +495,24 @@ void expireGenericCommand(redisClient *c, robj *key, robj *param, long offset) { addReply(c,shared.czero); return; } - if (seconds <= 0) { - if (dbDelete(c->db,key)) server.dirty++; - addReply(c, shared.cone); + /* EXPIRE with negative TTL, or EXPIREAT with a timestamp into the past + * should never be executed as a DEL when load the AOF or in the context + * 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); + addReply(c, shared.cone); return; } else { time_t when = time(NULL)+seconds; diff --git a/src/multi.c b/src/multi.c index ba3a0cd6caaa0cf96e7fdb883ee0f1ad902efdbb..f0b8fc8566e4c3021ba635142083d00864d87472 100644 --- a/src/multi.c +++ b/src/multi.c @@ -24,14 +24,14 @@ void freeClientMultiState(redisClient *c) { } /* Add a new command into the MULTI commands queue */ -void queueMultiCommand(redisClient *c, struct redisCommand *cmd) { +void queueMultiCommand(redisClient *c) { multiCmd *mc; int j; c->mstate.commands = zrealloc(c->mstate.commands, sizeof(multiCmd)*(c->mstate.count+1)); mc = c->mstate.commands+c->mstate.count; - mc->cmd = cmd; + mc->cmd = c->cmd; mc->argc = c->argc; mc->argv = zmalloc(sizeof(robj*)*c->argc); memcpy(mc->argv,c->argv,sizeof(robj*)*c->argc); @@ -78,6 +78,7 @@ void execCommand(redisClient *c) { int j; robj **orig_argv; int orig_argc; + struct redisCommand *orig_cmd; if (!(c->flags & REDIS_MULTI)) { addReplyError(c,"EXEC without MULTI"); @@ -105,18 +106,22 @@ void execCommand(redisClient *c) { unwatchAllKeys(c); /* Unwatch ASAP otherwise we'll waste CPU cycles */ orig_argv = c->argv; orig_argc = c->argc; + orig_cmd = c->cmd; addReplyMultiBulkLen(c,c->mstate.count); for (j = 0; j < c->mstate.count; j++) { c->argc = c->mstate.commands[j].argc; 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. */ c->mstate.commands[j].argc = c->argc; c->mstate.commands[j].argv = c->argv; + c->mstate.commands[j].cmd = c->cmd; } c->argv = orig_argv; c->argc = orig_argc; + c->cmd = orig_cmd; freeClientMultiState(c); initClientMultiState(c); c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS); diff --git a/src/networking.c b/src/networking.c index 7d4b963648a8ba792a40cba82de388bfb7f9722d..b31c89df9609f36c967c14741ba0281e4800be34 100644 --- a/src/networking.c +++ b/src/networking.c @@ -31,6 +31,7 @@ redisClient *createClient(int fd) { c->reqtype = 0; c->argc = 0; c->argv = NULL; + c->cmd = NULL; c->multibulklen = 0; c->bulklen = -1; c->sentlen = 0; @@ -444,6 +445,7 @@ static void freeClientArgv(redisClient *c) { for (j = 0; j < c->argc; j++) decrRefCount(c->argv[j]); c->argc = 0; + c->cmd = NULL; } void freeClient(redisClient *c) { @@ -896,4 +898,3 @@ void rewriteClientCommandVector(redisClient *c, int argc, ...) { c->argc = argc; va_end(ap); } - diff --git a/src/redis.c b/src/redis.c index 0ea7813a2c2d63f0213650d008457e75e36b383b..1d732e91f1bde6a534821025e79108a2fab5152e 100644 --- a/src/redis.c +++ b/src/redis.c @@ -677,7 +677,7 @@ void beforeSleep(struct aeEventLoop *eventLoop) { readQueryFromClient, c); cmd = lookupCommand(c->argv[0]->ptr); redisAssert(cmd != NULL); - call(c,cmd); + call(c); resetClient(c); /* There may be more data to process in the input buffer. */ if (c->querybuf && sdslen(c->querybuf) > 0) @@ -958,18 +958,18 @@ struct redisCommand *lookupCommandByCString(char *s) { } /* 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; dirty = server.dirty; - cmd->proc(c); + c->cmd->proc(c); dirty = server.dirty-dirty; duration = ustime()-start; slowlogPushEntryIfNeeded(c->argv,c->argc,duration); if (server.appendonly && dirty) - feedAppendOnlyFile(cmd,c->db->id,c->argv,c->argc); - if ((dirty || cmd->flags & REDIS_CMD_FORCE_REPLICATION) && + feedAppendOnlyFile(c->cmd,c->db->id,c->argv,c->argc); + if ((dirty || c->cmd->flags & REDIS_CMD_FORCE_REPLICATION) && listLength(server.slaves)) replicationFeedSlaves(server.slaves,c->db->id,c->argv,c->argc); if (listLength(server.monitors)) @@ -986,8 +986,6 @@ void call(redisClient *c, struct redisCommand *cmd) { * and other operations can be performed by the caller. Otherwise * if 0 is returned the client was destroied (i.e. after QUIT). */ int processCommand(redisClient *c) { - struct redisCommand *cmd; - /* The QUIT command is handled separately. Normal command procs will * go through checking for replication and QUIT will cause trouble * when FORCE_REPLICATION is enabled and would be implemented in @@ -999,21 +997,22 @@ int processCommand(redisClient *c) { } /* Now lookup the command and check ASAP about trivial error conditions - * such wrong arity, bad command name and so forth. */ - cmd = lookupCommand(c->argv[0]->ptr); - if (!cmd) { + * such as wrong arity, bad command name and so forth. */ + c->cmd = lookupCommand(c->argv[0]->ptr); + if (!c->cmd) { addReplyErrorFormat(c,"unknown command '%s'", (char*)c->argv[0]->ptr); return REDIS_OK; - } else if ((cmd->arity > 0 && cmd->arity != c->argc) || - (c->argc < -cmd->arity)) { + } else if ((c->cmd->arity > 0 && c->cmd->arity != c->argc) || + (c->argc < -c->cmd->arity)) { addReplyErrorFormat(c,"wrong number of arguments for '%s' command", - cmd->name); + c->cmd->name); return REDIS_OK; } /* 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"); return REDIS_OK; } @@ -1024,7 +1023,7 @@ int processCommand(redisClient *c) { * keys in the dataset). If there are not the only thing we can do * is returning an error. */ 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) { addReplyError(c,"command not allowed when used memory > 'maxmemory'"); @@ -1034,8 +1033,10 @@ int processCommand(redisClient *c) { /* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */ if ((dictSize(c->pubsub_channels) > 0 || listLength(c->pubsub_patterns) > 0) && - cmd->proc != subscribeCommand && cmd->proc != unsubscribeCommand && - cmd->proc != psubscribeCommand && cmd->proc != punsubscribeCommand) { + c->cmd->proc != subscribeCommand && + c->cmd->proc != unsubscribeCommand && + c->cmd->proc != psubscribeCommand && + c->cmd->proc != punsubscribeCommand) { addReplyError(c,"only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context"); return REDIS_OK; } @@ -1044,7 +1045,7 @@ int processCommand(redisClient *c) { * we are a slave with a broken link with master. */ if (server.masterhost && server.replstate != REDIS_REPL_CONNECTED && server.repl_serve_stale_data == 0 && - cmd->proc != infoCommand && cmd->proc != slaveofCommand) + c->cmd->proc != infoCommand && c->cmd->proc != slaveofCommand) { addReplyError(c, "link with MASTER is down and slave-serve-stale-data is set to no"); @@ -1052,22 +1053,22 @@ int processCommand(redisClient *c) { } /* 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); return REDIS_OK; } /* Exec the command */ if (c->flags & REDIS_MULTI && - cmd->proc != execCommand && cmd->proc != discardCommand && - cmd->proc != multiCommand && cmd->proc != watchCommand) + c->cmd->proc != execCommand && c->cmd->proc != discardCommand && + c->cmd->proc != multiCommand && c->cmd->proc != watchCommand) { - queueMultiCommand(c,cmd); + queueMultiCommand(c); addReply(c,shared.queued); } else { if (server.vm_enabled && server.vm_max_threads > 0 && - blockClientOnSwappedKeys(c,cmd)) return REDIS_ERR; - call(c,cmd); + blockClientOnSwappedKeys(c)) return REDIS_ERR; + call(c); } return REDIS_OK; } @@ -1075,20 +1076,29 @@ int processCommand(redisClient *c) { /*================================== Shutdown =============================== */ 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. We want to avoid race conditions, for instance our saving child may overwrite the synchronous saving did by SHUTDOWN. */ 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); rdbRemoveTempFile(server.bgsavechildpid); } 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 */ + redisLog(REDIS_NOTICE,"Calling fsync() on the AOF file."); 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 */ if (rdbSave(server.dbfilename) != REDIS_OK) { /* Ooops.. error saving! The best we can do is to continue @@ -1096,14 +1106,23 @@ int prepareForShutdown() { * in the next cron() Redis will be notified that the background * saving aborted, handling special stuff like slaves pending for * 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; } - } else { - redisLog(REDIS_WARNING,"Not saving DB."); } - if (server.daemonize) unlink(server.pidfile); - redisLog(REDIS_WARNING,"Server exit now, bye bye..."); + if (server.vm_enabled) { + 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; } @@ -1176,8 +1195,8 @@ sds genRedisInfoString(void) { "lru_clock:%ld\r\n" "used_cpu_sys:%.2f\r\n" "used_cpu_user:%.2f\r\n" - "used_cpu_sys_childrens:%.2f\r\n" - "used_cpu_user_childrens:%.2f\r\n" + "used_cpu_sys_children:%.2f\r\n" + "used_cpu_user_children:%.2f\r\n" "connected_clients:%d\r\n" "connected_slaves:%d\r\n" "client_longest_output_list:%lu\r\n" @@ -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++) { long long keys, vkeys; diff --git a/src/redis.h b/src/redis.h index 8a7a47fda2526dc8ea494e067f62030e4c209e48..1fea983f4c478a58f9af760b44a892e52f0758f8 100644 --- a/src/redis.h +++ b/src/redis.h @@ -314,6 +314,7 @@ typedef struct redisClient { sds querybuf; int argc; robj **argv; + struct redisCommand *cmd; int reqtype; int multibulklen; /* number of multi bulk arguments left to read */ long bulklen; /* length of bulk argument in multi bulk request */ @@ -700,7 +701,7 @@ void popGenericCommand(redisClient *c, int where); void unwatchAllKeys(redisClient *c); void initClientMultiState(redisClient *c); void freeClientMultiState(redisClient *c); -void queueMultiCommand(redisClient *c, struct redisCommand *cmd); +void queueMultiCommand(redisClient *c); void touchWatchedKey(redisDb *db, robj *key); void touchWatchedKeysOnFlush(int dbid); @@ -788,7 +789,7 @@ int processCommand(redisClient *c); void setupSignalHandlers(void); struct redisCommand *lookupCommand(sds name); struct redisCommand *lookupCommandByCString(char *s); -void call(redisClient *c, struct redisCommand *cmd); +void call(redisClient *c); int prepareForShutdown(); void redisLog(int level, const char *fmt, ...); void usage(); @@ -819,7 +820,7 @@ void vmReopenSwapFile(void); int vmFreePage(off_t page); void zunionInterBlockClientOnSwappedKeys(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); void handleClientsBlockedOnSwappedKey(redisDb *db, robj *key); vmpointer *vmSwapObjectBlocking(robj *val); diff --git a/src/t_list.c b/src/t_list.c index c05bd1ae306b922d41f4857a23f18a93850c91a2..b17bfbdc13d3fa17e91f69858c483cd6efbbde43 100644 --- a/src/t_list.c +++ b/src/t_list.c @@ -904,6 +904,7 @@ void blockingPopGenericCommand(redisClient *c, int where) { if (listTypeLength(o) != 0) { /* If the list contains elements fall back to the usual * non-blocking POP operation */ + struct redisCommand *orig_cmd; robj *argv[2], **orig_argv; int orig_argc; @@ -911,6 +912,7 @@ void blockingPopGenericCommand(redisClient *c, int where) { * popGenericCommand() as the command takes a single key. */ orig_argv = c->argv; orig_argc = c->argc; + orig_cmd = c->cmd; argv[1] = c->argv[j]; c->argv = argv; c->argc = 2; @@ -928,6 +930,7 @@ void blockingPopGenericCommand(redisClient *c, int where) { /* Fix the client structure with the original stuff */ c->argv = orig_argv; c->argc = orig_argc; + c->cmd = orig_cmd; return; } diff --git a/src/version.h b/src/version.h index 90686116ed4615548fcd1d9c63439ea781ebfb2e..f43af6e62a8a1daad3832386f8640f7246698315 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define REDIS_VERSION "2.2.11" +#define REDIS_VERSION "2.2.12" diff --git a/src/vm.c b/src/vm.c index ac0d92e332cf091b88c5caeb2b3a9b439869a2a5..3778856f890ffa94cba5a1d19f46c6f971bbae33 100644 --- a/src/vm.c +++ b/src/vm.c @@ -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 * continue as the keys it is going to access appear to be in memory. */ -int blockClientOnSwappedKeys(redisClient *c, struct redisCommand *cmd) { - if (cmd->vm_preload_proc != NULL) { - cmd->vm_preload_proc(c,cmd,c->argc,c->argv); +int blockClientOnSwappedKeys(redisClient *c) { + if (c->cmd->vm_preload_proc != NULL) { + c->cmd->vm_preload_proc(c,c->cmd,c->argc,c->argv); } 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. */ diff --git a/src/zmalloc.c b/src/zmalloc.c index aa3ccfe963d8fa6e67eacca70dc2ce953b973c27..af2ffda21ab660575969b62dcccdf297b44e5856 100644 --- a/src/zmalloc.c +++ b/src/zmalloc.c @@ -55,16 +55,13 @@ #define update_zmalloc_stat_alloc(__n,__size) do { \ 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 (zmalloc_thread_safe) { \ pthread_mutex_lock(&used_memory_mutex); \ used_memory += _n; \ - zmalloc_allocations[_stat_slot]++; \ pthread_mutex_unlock(&used_memory_mutex); \ } else { \ used_memory += _n; \ - zmalloc_allocations[_stat_slot]++; \ } \ } while(0) @@ -83,8 +80,6 @@ static size_t used_memory = 0; static int zmalloc_thread_safe = 0; 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) { fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n", @@ -185,11 +180,6 @@ size_t zmalloc_used_memory(void) { 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) { zmalloc_thread_safe = 1; } diff --git a/src/zmalloc.h b/src/zmalloc.h index d19979a30adafb4faf79e741a652a22f6a166e94..bb6f629af78616e09cf0086380f436794e3b351c 100644 --- a/src/zmalloc.h +++ b/src/zmalloc.h @@ -40,8 +40,5 @@ size_t zmalloc_used_memory(void); void zmalloc_enable_thread_safeness(void); float zmalloc_get_fragmentation_ratio(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 */ diff --git a/tests/integration/aof.tcl b/tests/integration/aof.tcl index 927969b627525f5f774b6226f92949df1cbb00a3..954edc2c4959646ae5583313efb4a75d79596fa7 100644 --- a/tests/integration/aof.tcl +++ b/tests/integration/aof.tcl @@ -101,4 +101,22 @@ tags {"aof"} { 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] + } + } } diff --git a/tests/test_helper.tcl b/tests/test_helper.tcl index 9b1ba6347c1a11babf7409e6def8ccf9369a3532..353c99a473996fcf108e776039a57d5d1e27bd57 100644 --- a/tests/test_helper.tcl +++ b/tests/test_helper.tcl @@ -128,7 +128,7 @@ proc execute_everything {} { execute_tests "unit/slowlog" # 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/basic" execute_tests "unit/type/list"