Commit 0681c5ad authored by antirez's avatar antirez
Browse files

master branch merged into scripting.

parents 15bc1cc1 34a8b517
...@@ -14,6 +14,7 @@ clean: ...@@ -14,6 +14,7 @@ clean:
cd deps/linenoise && $(MAKE) $@ cd deps/linenoise && $(MAKE) $@
cd deps/jemalloc && $(MAKE) distclean cd deps/jemalloc && $(MAKE) distclean
cd deps/lua && $(MAKE) $@ cd deps/lua && $(MAKE) $@
-(cd deps/jemalloc && $(MAKE) distclean)
$(TARGETS): $(TARGETS):
cd src && $(MAKE) $@ cd src && $(MAKE) $@
......
...@@ -50,7 +50,7 @@ typedef struct redisReader { ...@@ -50,7 +50,7 @@ typedef struct redisReader {
size_t pos; /* buffer cursor */ size_t pos; /* buffer cursor */
size_t len; /* buffer length */ size_t len; /* buffer length */
redisReadTask rstack[3]; /* stack of read tasks */ redisReadTask rstack[9]; /* stack of read tasks */
int ridx; /* index of stack */ int ridx; /* index of stack */
void *privdata; /* user-settable arbitrary field */ void *privdata; /* user-settable arbitrary field */
} redisReader; } redisReader;
...@@ -347,9 +347,9 @@ static int processMultiBulkItem(redisReader *r) { ...@@ -347,9 +347,9 @@ static int processMultiBulkItem(redisReader *r) {
int root = 0; int root = 0;
/* Set error for nested multi bulks with depth > 1 */ /* Set error for nested multi bulks with depth > 1 */
if (r->ridx == 2) { if (r->ridx == 8) {
redisSetReplyReaderError(r,sdscatprintf(sdsempty(), redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
"No support for nested multi bulk replies with depth > 1")); "No support for nested multi bulk replies with depth > 7"));
return -1; return -1;
} }
......
...@@ -319,6 +319,30 @@ auto-aof-rewrite-min-size 64mb ...@@ -319,6 +319,30 @@ auto-aof-rewrite-min-size 64mb
# your server forever. Set it to 0 or a negative value for unlimited execution. # your server forever. Set it to 0 or a negative value for unlimited execution.
lua-time-limit 60000 lua-time-limit 60000
################################## SLOW LOG ###################################
# The Redis Slow Log is a system to log queries that exceeded a specified
# execution time. The execution time does not include the I/O operations
# like talking with the client, sending the reply and so forth,
# but just the time needed to actually execute the command (this is the only
# stage of command execution where the thread is blocked and can not serve
# other requests in the meantime).
#
# You can configure the slow log with two parameters: one tells Redis
# what is the execution time, in microseconds, to exceed in order for the
# command to get logged, and the other parameter is the length of the
# slow log. When a new command is logged the oldest one is removed from the
# queue of logged commands.
# The following time is expressed in microseconds, so 1000000 is equivalent
# to one second. Note that a negative number disables the slow log, while
# a value of zero forces the logging of every command.
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-max-len 1024
############################### ADVANCED CONFIG ############################### ############################### ADVANCED CONFIG ###############################
# Hashes are encoded in a special way (much more memory efficient) when they # Hashes are encoded in a special way (much more memory efficient) when they
......
...@@ -61,7 +61,7 @@ QUIET_CC = @printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR ...@@ -61,7 +61,7 @@ QUIET_CC = @printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR
QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR); QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR);
endif endif
OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endian.o scripting.o OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endian.o slowlog.o scripting.o
BENCHOBJ = ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o BENCHOBJ = ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o
CLIOBJ = anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o CLIOBJ = anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o
CHECKDUMPOBJ = redis-check-dump.o lzf_c.o lzf_d.o CHECKDUMPOBJ = redis-check-dump.o lzf_c.o lzf_d.o
...@@ -86,38 +86,37 @@ ae_kqueue.o: ae_kqueue.c ...@@ -86,38 +86,37 @@ ae_kqueue.o: ae_kqueue.c
ae_select.o: ae_select.c ae_select.o: ae_select.c
anet.o: anet.c fmacros.h anet.h anet.o: anet.c fmacros.h anet.h
aof.o: aof.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ aof.o: aof.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h slowlog.h
cluster.o: cluster.c redis.h fmacros.h config.h ae.h sds.h dict.h \ cluster.o: cluster.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h \
slowlog.h
config.o: config.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ config.o: config.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h slowlog.h
crc16.o: crc16.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ crc16.o: crc16.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h slowlog.h
db.o: db.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ db.o: db.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h slowlog.h
debug.o: debug.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ debug.o: debug.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h sha1.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h slowlog.h \
dict.o: dict.c fmacros.h dict.h zmalloc.h
diskstore.o: diskstore.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h \
sha1.h sha1.h
dscache.o: dscache.c redis.h fmacros.h config.h ae.h sds.h dict.h \ dict.o: dict.c fmacros.h dict.h zmalloc.h
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
endian.o: endian.c endian.o: endian.c
intset.o: intset.c intset.h zmalloc.h endian.h intset.o: intset.c intset.h zmalloc.h endian.h
lzf_c.o: lzf_c.c lzfP.h lzf_c.o: lzf_c.c lzfP.h
lzf_d.o: lzf_d.c lzfP.h lzf_d.o: lzf_d.c lzfP.h
multi.o: multi.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ multi.o: multi.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h slowlog.h
networking.o: networking.c redis.h fmacros.h config.h ae.h sds.h dict.h \ networking.o: networking.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h \
slowlog.h
object.o: object.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ object.o: object.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h slowlog.h
pqsort.o: pqsort.c pqsort.o: pqsort.c
pubsub.o: pubsub.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ pubsub.o: pubsub.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h slowlog.h
rdb.o: rdb.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ rdb.o: rdb.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h lzf.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h slowlog.h \
lzf.h
redis-benchmark.o: redis-benchmark.c fmacros.h ae.h \ redis-benchmark.o: redis-benchmark.c fmacros.h ae.h \
../deps/hiredis/hiredis.h sds.h adlist.h zmalloc.h ../deps/hiredis/hiredis.h sds.h adlist.h zmalloc.h
redis-check-aof.o: redis-check-aof.c fmacros.h config.h redis-check-aof.o: redis-check-aof.c fmacros.h config.h
...@@ -125,27 +124,33 @@ redis-check-dump.o: redis-check-dump.c lzf.h ...@@ -125,27 +124,33 @@ redis-check-dump.o: redis-check-dump.c lzf.h
redis-cli.o: redis-cli.c fmacros.h version.h ../deps/hiredis/hiredis.h \ redis-cli.o: redis-cli.c fmacros.h version.h ../deps/hiredis/hiredis.h \
sds.h zmalloc.h ../deps/linenoise/linenoise.h help.h sds.h zmalloc.h ../deps/linenoise/linenoise.h help.h
redis.o: redis.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ redis.o: redis.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h \ zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h slowlog.h \
asciilogo.h asciilogo.h
release.o: release.c release.h release.o: release.c release.h
replication.o: replication.c redis.h fmacros.h config.h ae.h sds.h dict.h \ replication.o: replication.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h \
slowlog.h
sds.o: sds.c sds.h zmalloc.h sds.o: sds.c sds.h zmalloc.h
sha1.o: sha1.c sha1.h config.h sha1.o: sha1.c sha1.h config.h
slowlog.o: slowlog.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h \
slowlog.h
sort.o: sort.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ sort.o: sort.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h pqsort.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h slowlog.h \
pqsort.h
syncio.o: syncio.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ syncio.o: syncio.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h slowlog.h
t_hash.o: t_hash.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ t_hash.o: t_hash.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h slowlog.h
t_list.o: t_list.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ t_list.o: t_list.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h slowlog.h
t_set.o: t_set.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ t_set.o: t_set.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h slowlog.h
t_string.o: t_string.c redis.h fmacros.h config.h ae.h sds.h dict.h \ t_string.o: t_string.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h \
slowlog.h
t_zset.o: t_zset.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \ t_zset.o: t_zset.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h slowlog.h
util.o: util.c fmacros.h util.h util.o: util.c fmacros.h util.h
ziplist.o: ziplist.c zmalloc.h util.h ziplist.h endian.h ziplist.o: ziplist.c zmalloc.h util.h ziplist.h endian.h
zipmap.o: zipmap.c zmalloc.h endian.h zipmap.o: zipmap.c zmalloc.h endian.h
...@@ -197,8 +202,8 @@ clean: ...@@ -197,8 +202,8 @@ clean:
dep: dep:
$(CC) -MM *.c -I ../deps/hiredis -I ../deps/linenoise $(CC) -MM *.c -I ../deps/hiredis -I ../deps/linenoise
test: redis-server test: redis-server redis-check-aof
@(cd ..; (which tclsh >/dev/null && tclsh tests/test_helper.tcl --tags "${TAGS}" --file "${FILE}") || echo "You need to install Tcl in order to run tests.") @(cd ..; (which tclsh8.5 >/dev/null && tclsh8.5 tests/test_helper.tcl --tags "${TAGS}") || echo "You need to install Tcl (tclsh8.5) in order to run tests.")
bench: bench:
./redis-benchmark ./redis-benchmark
......
...@@ -287,6 +287,8 @@ int loadAppendOnlyFile(char *filename) { ...@@ -287,6 +287,8 @@ int loadAppendOnlyFile(char *filename) {
/* The fake client should not have a reply */ /* The fake client should not have a reply */
redisAssert(fakeClient->bufpos == 0 && listLength(fakeClient->reply) == 0); redisAssert(fakeClient->bufpos == 0 && listLength(fakeClient->reply) == 0);
/* The fake client should never get blocked */
redisAssert((fakeClient->flags & REDIS_BLOCKED) == 0);
/* Clean up. Command code may have changed argv/argc so we use the /* Clean up. Command code may have changed argv/argc so we use the
* argv/argc of the client instead of the local variables. */ * argv/argc of the client instead of the local variables. */
......
...@@ -298,6 +298,12 @@ void loadServerConfig(char *filename) { ...@@ -298,6 +298,12 @@ void loadServerConfig(char *filename) {
server.cluster.configfile = zstrdup(argv[1]); server.cluster.configfile = zstrdup(argv[1]);
} else if (!strcasecmp(argv[0],"lua-time-limit") && argc == 2) { } else if (!strcasecmp(argv[0],"lua-time-limit") && argc == 2) {
server.lua_time_limit = strtoll(argv[1],NULL,10); server.lua_time_limit = strtoll(argv[1],NULL,10);
} else if (!strcasecmp(argv[0],"slowlog-log-slower-than") &&
argc == 2)
{
server.slowlog_log_slower_than = strtoll(argv[1],NULL,10);
} else if (!strcasecmp(argv[0],"slowlog-max-len") && argc == 2) {
server.slowlog_max_len = strtoll(argv[1],NULL,10);
} else { } else {
err = "Bad directive or wrong number of arguments"; goto loaderr; err = "Bad directive or wrong number of arguments"; goto loaderr;
} }
...@@ -471,6 +477,12 @@ void configSetCommand(redisClient *c) { ...@@ -471,6 +477,12 @@ void configSetCommand(redisClient *c) {
} else if (!strcasecmp(c->argv[2]->ptr,"lua-time-limit")) { } else if (!strcasecmp(c->argv[2]->ptr,"lua-time-limit")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.lua_time_limit = ll; server.lua_time_limit = ll;
} else if (!strcasecmp(c->argv[2]->ptr,"slowlog-log-slower-than")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR) goto badfmt;
server.slowlog_log_slower_than = ll;
} else if (!strcasecmp(c->argv[2]->ptr,"slowlog-max-len")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.slowlog_max_len = (unsigned)ll;
} else { } else {
addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s", addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
(char*)c->argv[2]->ptr); (char*)c->argv[2]->ptr);
...@@ -645,6 +657,14 @@ void configGetCommand(redisClient *c) { ...@@ -645,6 +657,14 @@ void configGetCommand(redisClient *c) {
if (stringmatch(pattern,"lua-time-limit",0)) { if (stringmatch(pattern,"lua-time-limit",0)) {
addReplyBulkCString(c,"lua-time-limit"); addReplyBulkCString(c,"lua-time-limit");
addReplyBulkLongLong(c,server.lua_time_limit); addReplyBulkLongLong(c,server.lua_time_limit);
if (stringmatch(pattern,"slowlog-log-slower-than",0)) {
addReplyBulkCString(c,"slowlog-log-slower-than");
addReplyBulkLongLong(c,server.slowlog_log_slower_than);
matches++;
}
if (stringmatch(pattern,"slowlog-max-len",0)) {
addReplyBulkCString(c,"slowlog-max-len");
addReplyBulkLongLong(c,server.slowlog_max_len);
matches++; matches++;
} }
setDeferredMultiBulkLength(c,replylen,matches*2); setDeferredMultiBulkLength(c,replylen,matches*2);
......
...@@ -476,6 +476,9 @@ int expireIfNeeded(redisDb *db, robj *key) { ...@@ -476,6 +476,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.
...@@ -513,10 +516,24 @@ void expireGenericCommand(redisClient *c, robj *key, robj *param, long offset) { ...@@ -513,10 +516,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);
signalModifiedKey(c->db,key); signalModifiedKey(c->db,key);
addReply(c, shared.cone);
return; return;
} else { } else {
time_t when = time(NULL)+seconds; time_t when = time(NULL)+seconds;
......
...@@ -281,6 +281,12 @@ void debugCommand(redisClient *c) { ...@@ -281,6 +281,12 @@ void debugCommand(redisClient *c) {
d = sdscatprintf(d, "%02x",digest[j]); d = sdscatprintf(d, "%02x",digest[j]);
addReplyStatus(c,d); addReplyStatus(c,d);
sdsfree(d); sdsfree(d);
} else if (!strcasecmp(c->argv[1]->ptr,"sleep") && c->argc == 3) {
double dtime = strtod(c->argv[2]->ptr,NULL);
long long utime = dtime*1000000;
usleep(utime);
addReply(c,shared.ok);
} else { } else {
addReplyError(c, addReplyError(c,
"Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|SWAPIN <key>|SWAPOUT <key>|RELOAD]"); "Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|SWAPIN <key>|SWAPOUT <key>|RELOAD]");
......
...@@ -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);
......
...@@ -36,6 +36,7 @@ redisClient *createClient(int fd) { ...@@ -36,6 +36,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;
...@@ -461,6 +462,7 @@ static void freeClientArgv(redisClient *c) { ...@@ -461,6 +462,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) {
...@@ -961,5 +963,7 @@ void rewriteClientCommandVector(redisClient *c, int argc, ...) { ...@@ -961,5 +963,7 @@ void rewriteClientCommandVector(redisClient *c, int argc, ...) {
/* Replace argv and argc with our new versions. */ /* Replace argv and argc with our new versions. */
c->argv = argv; c->argv = argv;
c->argc = argc; c->argc = argc;
c->cmd = lookupCommand(c->argv[0]->ptr);
redisAssert(c->cmd != NULL);
va_end(ap); va_end(ap);
} }
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
*/ */
#include "redis.h" #include "redis.h"
#include "slowlog.h"
#ifdef HAVE_BACKTRACE #ifdef HAVE_BACKTRACE
#include <execinfo.h> #include <execinfo.h>
...@@ -194,7 +195,8 @@ struct redisCommand redisCommandTable[] = { ...@@ -194,7 +195,8 @@ struct redisCommand redisCommandTable[] = {
{"object",objectCommand,-2,0,NULL,0,0,0,0,0}, {"object",objectCommand,-2,0,NULL,0,0,0,0,0},
{"client",clientCommand,-2,0,NULL,0,0,0,0,0}, {"client",clientCommand,-2,0,NULL,0,0,0,0,0},
{"eval",evalCommand,-3,REDIS_CMD_DENYOOM,zunionInterGetKeys,0,0,0,0,0}, {"eval",evalCommand,-3,REDIS_CMD_DENYOOM,zunionInterGetKeys,0,0,0,0,0},
{"evalsha",evalShaCommand,-3,REDIS_CMD_DENYOOM,zunionInterGetKeys,0,0,0,0,0} {"evalsha",evalShaCommand,-3,REDIS_CMD_DENYOOM,zunionInterGetKeys,0,0,0,0,0},
{"slowlog",slowlogCommand,-2,0,NULL,0,0,0,0,0}
}; };
/*============================ Utility functions ============================ */ /*============================ Utility functions ============================ */
...@@ -871,6 +873,10 @@ void initServerConfig() { ...@@ -871,6 +873,10 @@ void initServerConfig() {
populateCommandTable(); populateCommandTable();
server.delCommand = lookupCommandByCString("del"); server.delCommand = lookupCommandByCString("del");
server.multiCommand = lookupCommandByCString("multi"); server.multiCommand = lookupCommandByCString("multi");
/* Slow log */
server.slowlog_log_slower_than = REDIS_SLOWLOG_LOG_SLOWER_THAN;
server.slowlog_max_len = REDIS_SLOWLOG_MAX_LEN;
} }
void initServer() { void initServer() {
...@@ -958,6 +964,7 @@ void initServer() { ...@@ -958,6 +964,7 @@ void initServer() {
if (server.cluster_enabled) clusterInit(); if (server.cluster_enabled) clusterInit();
scriptingInit(); scriptingInit();
slowlogInit();
srand(time(NULL)^getpid()); srand(time(NULL)^getpid());
} }
...@@ -1004,18 +1011,20 @@ struct redisCommand *lookupCommandByCString(char *s) { ...@@ -1004,18 +1011,20 @@ 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(); 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;
cmd->microseconds += ustime()-start; duration = ustime()-start;
cmd->calls++; c->cmd->microseconds += duration;
slowlogPushEntryIfNeeded(c->argv,c->argc,duration);
c->cmd->calls++;
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))
...@@ -1032,8 +1041,6 @@ void call(redisClient *c, struct redisCommand *cmd) { ...@@ -1032,8 +1041,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
...@@ -1045,28 +1052,29 @@ int processCommand(redisClient *c) { ...@@ -1045,28 +1052,29 @@ 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;
} }
/* If cluster is enabled, redirect here */ /* If cluster is enabled, redirect here */
if (server.cluster_enabled && if (server.cluster_enabled &&
!(cmd->getkeys_proc == NULL && cmd->firstkey == 0)) { !(c->cmd->getkeys_proc == NULL && c->cmd->firstkey == 0)) {
int hashslot; int hashslot;
if (server.cluster.state != REDIS_CLUSTER_OK) { if (server.cluster.state != REDIS_CLUSTER_OK) {
...@@ -1074,7 +1082,7 @@ int processCommand(redisClient *c) { ...@@ -1074,7 +1082,7 @@ int processCommand(redisClient *c) {
return REDIS_OK; return REDIS_OK;
} else { } else {
int ask; int ask;
clusterNode *n = getNodeByQuery(c,cmd,c->argv,c->argc,&hashslot,&ask); clusterNode *n = getNodeByQuery(c,c->cmd,c->argv,c->argc,&hashslot,&ask);
if (n == NULL) { if (n == NULL) {
addReplyError(c,"Multi keys request invalid in cluster"); addReplyError(c,"Multi keys request invalid in cluster");
return REDIS_OK; return REDIS_OK;
...@@ -1093,7 +1101,7 @@ int processCommand(redisClient *c) { ...@@ -1093,7 +1101,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'");
...@@ -1103,8 +1111,10 @@ int processCommand(redisClient *c) { ...@@ -1103,8 +1111,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;
} }
...@@ -1113,7 +1123,7 @@ int processCommand(redisClient *c) { ...@@ -1113,7 +1123,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");
...@@ -1121,20 +1131,20 @@ int processCommand(redisClient *c) { ...@@ -1121,20 +1131,20 @@ 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 {
call(c,cmd); call(c);
} }
return REDIS_OK; return REDIS_OK;
} }
...@@ -1305,22 +1315,6 @@ sds genRedisInfoString(char *section) { ...@@ -1305,22 +1315,6 @@ sds genRedisInfoString(char *section) {
); );
} }
/* Allocation statistics */
if (allsections || !strcasecmp(section,"allocstats")) {
if (sections++) info = sdscat(info,"\r\n");
info = sdscat(info, "# Allocstats\r\nallocation_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");
}
/* Persistence */ /* Persistence */
if (allsections || defsections || !strcasecmp(section,"persistence")) { if (allsections || defsections || !strcasecmp(section,"persistence")) {
if (sections++) info = sdscat(info,"\r\n"); if (sections++) info = sdscat(info,"\r\n");
...@@ -1456,8 +1450,8 @@ sds genRedisInfoString(char *section) { ...@@ -1456,8 +1450,8 @@ sds genRedisInfoString(char *section) {
"# CPU\r\n" "# CPU\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",
(float)self_ru.ru_utime.tv_sec+(float)self_ru.ru_utime.tv_usec/1000000, (float)self_ru.ru_utime.tv_sec+(float)self_ru.ru_utime.tv_usec/1000000,
(float)self_ru.ru_stime.tv_sec+(float)self_ru.ru_stime.tv_usec/1000000, (float)self_ru.ru_stime.tv_sec+(float)self_ru.ru_stime.tv_usec/1000000,
(float)c_ru.ru_utime.tv_sec+(float)c_ru.ru_utime.tv_usec/1000000, (float)c_ru.ru_utime.tv_sec+(float)c_ru.ru_utime.tv_usec/1000000,
......
...@@ -21,17 +21,17 @@ ...@@ -21,17 +21,17 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <lua.h> #include <lua.h>
#include "ae.h" /* Event driven programming library */ #include "ae.h" /* Event driven programming library */
#include "sds.h" /* Dynamic safe strings */ #include "sds.h" /* Dynamic safe strings */
#include "dict.h" /* Hash tables */ #include "dict.h" /* Hash tables */
#include "adlist.h" /* Linked lists */ #include "adlist.h" /* Linked lists */
#include "zmalloc.h" /* total memory usage aware version of malloc/free */ #include "zmalloc.h" /* total memory usage aware version of malloc/free */
#include "anet.h" /* Networking the easy way */ #include "anet.h" /* Networking the easy way */
#include "zipmap.h" /* Compact string -> string data structure */ #include "zipmap.h" /* Compact string -> string data structure */
#include "ziplist.h" /* Compact list data structure */ #include "ziplist.h" /* Compact list data structure */
#include "intset.h" /* Compact integer set structure */ #include "intset.h" /* Compact integer set structure */
#include "version.h" #include "version.h" /* Version macro */
#include "util.h" #include "util.h" /* Misc functions useful in many places */
/* Error codes */ /* Error codes */
#define REDIS_OK 0 #define REDIS_OK 0
...@@ -53,6 +53,8 @@ ...@@ -53,6 +53,8 @@
#define REDIS_MAX_LOGMSG_LEN 1024 /* Default maximum length of syslog messages */ #define REDIS_MAX_LOGMSG_LEN 1024 /* Default maximum length of syslog messages */
#define REDIS_AUTO_AOFREWRITE_PERC 100 #define REDIS_AUTO_AOFREWRITE_PERC 100
#define REDIS_AUTO_AOFREWRITE_MIN_SIZE (1024*1024) #define REDIS_AUTO_AOFREWRITE_MIN_SIZE (1024*1024)
#define REDIS_SLOWLOG_LOG_SLOWER_THAN 10000
#define REDIS_SLOWLOG_MAX_LEN 64
/* Hash table parameters */ /* Hash table parameters */
#define REDIS_HT_MINFILL 10 /* Minimal hash table fill 10% */ #define REDIS_HT_MINFILL 10 /* Minimal hash table fill 10% */
...@@ -312,6 +314,7 @@ typedef struct redisClient { ...@@ -312,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 */
...@@ -530,6 +533,10 @@ struct redisServer { ...@@ -530,6 +533,10 @@ struct redisServer {
long long stat_keyspace_misses; /* number of failed lookups of keys */ long long stat_keyspace_misses; /* number of failed lookups of keys */
size_t stat_peak_memory; /* max used memory record */ size_t stat_peak_memory; /* max used memory record */
long long stat_fork_time; /* time needed to perform latets fork() */ long long stat_fork_time; /* time needed to perform latets fork() */
list *slowlog;
long long slowlog_entry_id;
long long slowlog_log_slower_than;
unsigned long slowlog_max_len;
/* Configuration */ /* Configuration */
int verbosity; int verbosity;
int maxidletime; int maxidletime;
...@@ -807,7 +814,7 @@ void popGenericCommand(redisClient *c, int where); ...@@ -807,7 +814,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);
...@@ -918,7 +925,7 @@ int processCommand(redisClient *c); ...@@ -918,7 +925,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 redisLogRaw(int level, const char *msg); void redisLogRaw(int level, const char *msg);
......
#include "redis.h"
#include "slowlog.h"
/* Slowlog implements a system that is able to remember the latest N
* queries that took more than M microseconds to execute.
*
* The execution time to reach to be logged in the slow log is set
* using the 'slowlog-log-slower-than' config directive, that is also
* readable and writable using the CONFIG SET/GET command.
*
* The slow queries log is actually not "logged" in the Redis log file
* but is accessible thanks to the SLOWLOG command. */
/* Create a new slowlog entry.
* Incrementing the ref count of all the objects retained is up to
* this function. */
slowlogEntry *slowlogCreateEntry(robj **argv, int argc, long long duration) {
slowlogEntry *se = zmalloc(sizeof(*se));
int j;
se->argc = argc;
se->argv = zmalloc(sizeof(robj*)*argc);
for (j = 0; j < argc; j++) {
se->argv[j] = argv[j];
incrRefCount(argv[j]);
}
se->time = time(NULL);
se->duration = duration;
se->id = server.slowlog_entry_id++;
return se;
}
/* Free a slow log entry. The argument is void so that the prototype of this
* function matches the one of the 'free' method of adlist.c.
*
* This function will take care to release all the retained object. */
void slowlogFreeEntry(void *septr) {
slowlogEntry *se = septr;
int j;
for (j = 0; j < se->argc; j++)
decrRefCount(se->argv[j]);
zfree(se->argv);
zfree(se);
}
/* Initialize the slow log. This function should be called a single time
* at server startup. */
void slowlogInit(void) {
server.slowlog = listCreate();
server.slowlog_entry_id = 0;
listSetFreeMethod(server.slowlog,slowlogFreeEntry);
}
/* Push a new entry into the slow log.
* This function will make sure to trim the slow log accordingly to the
* configured max length. */
void slowlogPushEntryIfNeeded(robj **argv, int argc, long long duration) {
if (server.slowlog_log_slower_than < 0) return; /* Slowlog disabled */
if (duration >= server.slowlog_log_slower_than)
listAddNodeHead(server.slowlog,slowlogCreateEntry(argv,argc,duration));
/* Remove old entries if needed. */
while (listLength(server.slowlog) > server.slowlog_max_len)
listDelNode(server.slowlog,listLast(server.slowlog));
}
/* Remove all the entries from the current slow log. */
void slowlogReset(void) {
while (listLength(server.slowlog) > 0)
listDelNode(server.slowlog,listLast(server.slowlog));
}
/* The SLOWLOG command. Implements all the subcommands needed to handle the
* Redis slow log. */
void slowlogCommand(redisClient *c) {
if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"reset")) {
slowlogReset();
addReply(c,shared.ok);
} else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"len")) {
addReplyLongLong(c,listLength(server.slowlog));
} else if ((c->argc == 2 || c->argc == 3) &&
!strcasecmp(c->argv[1]->ptr,"get"))
{
long count = 10, sent = 0;
listIter li;
void *totentries;
listNode *ln;
slowlogEntry *se;
if (c->argc == 3 &&
getLongFromObjectOrReply(c,c->argv[2],&count,NULL) != REDIS_OK)
return;
listRewind(server.slowlog,&li);
totentries = addDeferredMultiBulkLength(c);
while(count-- && (ln = listNext(&li))) {
int j;
se = ln->value;
addReplyMultiBulkLen(c,4);
addReplyLongLong(c,se->id);
addReplyLongLong(c,se->time);
addReplyLongLong(c,se->duration);
addReplyMultiBulkLen(c,se->argc);
for (j = 0; j < se->argc; j++)
addReplyBulk(c,se->argv[j]);
sent++;
}
setDeferredMultiBulkLength(c,totentries,sent);
} else {
addReplyError(c,
"Unknown SLOWLOG subcommand or wrong # of args. Try GET, RESET, LEN.");
}
}
/* This structure defines an entry inside the slow log list */
typedef struct slowlogEntry {
robj **argv;
int argc;
long long id; /* Unique entry identifier. */
long long duration; /* Time spent by the query, in nanoseconds. */
time_t time; /* Unix time at which the query was executed. */
} slowlogEntry;
/* Exported API */
void slowlogInit(void);
void slowlogPushEntryIfNeeded(robj **argv, int argc, long long duration);
/* Exported commands */
void slowlogCommand(redisClient *c);
...@@ -910,6 +910,7 @@ void blockingPopGenericCommand(redisClient *c, int where) { ...@@ -910,6 +910,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;
...@@ -917,6 +918,7 @@ void blockingPopGenericCommand(redisClient *c, int where) { ...@@ -917,6 +918,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;
...@@ -934,6 +936,7 @@ void blockingPopGenericCommand(redisClient *c, int where) { ...@@ -934,6 +936,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;
} }
......
...@@ -3,3 +3,15 @@ ...@@ -3,3 +3,15 @@
Memcheck:Cond Memcheck:Cond
fun:lzf_compress fun:lzf_compress
} }
{
<lzf_unitialized_hash_table>
Memcheck:Value4
fun:lzf_compress
}
{
<lzf_unitialized_hash_table>
Memcheck:Value8
fun:lzf_compress
}
...@@ -60,16 +60,13 @@ ...@@ -60,16 +60,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)
...@@ -88,8 +85,6 @@ ...@@ -88,8 +85,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",
...@@ -190,11 +185,6 @@ size_t zmalloc_used_memory(void) { ...@@ -190,11 +185,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;
} }
......
...@@ -75,8 +75,5 @@ size_t zmalloc_used_memory(void); ...@@ -75,8 +75,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 */
...@@ -32,6 +32,7 @@ tags {"aof"} { ...@@ -32,6 +32,7 @@ tags {"aof"} {
start_server_aof [list dir $server_path] { start_server_aof [list dir $server_path] {
test "Unfinished MULTI: Server should not have been started" { test "Unfinished MULTI: Server should not have been started" {
if {$::valgrind} {after 2000}
assert_equal 0 [is_alive $srv] assert_equal 0 [is_alive $srv]
} }
...@@ -49,6 +50,7 @@ tags {"aof"} { ...@@ -49,6 +50,7 @@ tags {"aof"} {
start_server_aof [list dir $server_path] { start_server_aof [list dir $server_path] {
test "Short read: Server should not have been started" { test "Short read: Server should not have been started" {
if {$::valgrind} {after 2000}
assert_equal 0 [is_alive $srv] assert_equal 0 [is_alive $srv]
} }
...@@ -101,4 +103,29 @@ tags {"aof"} { ...@@ -101,4 +103,29 @@ 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]
}
}
start_server {overrides {appendonly {yes} appendfilename {appendonly.aof}}} {
test {Redis should not try to convert DEL into EXPIREAT for EXPIRE -1} {
r set x 10
r expire x -1
}
}
} }
start_server {tags {"repl"}} {
start_server {} {
test {First server should have role slave after SLAVEOF} {
r -1 slaveof [srv 0 host] [srv 0 port]
after 1000
s -1 role
} {slave}
test {MASTER and SLAVE dataset should be identical after complex ops} {
createComplexDataset r 10000
after 500
if {[r debug digest] ne [r -1 debug digest]} {
set csv1 [csvdump r]
set csv2 [csvdump {r -1}]
set fd [open /tmp/repldump1.txt w]
puts -nonewline $fd $csv1
close $fd
set fd [open /tmp/repldump2.txt w]
puts -nonewline $fd $csv2
close $fd
puts "Master - Slave inconsistency"
puts "Run diff -u against /tmp/repldump*.txt for more info"
}
assert_equal [r debug digest] [r -1 debug digest]
}
}
}
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