mirror of
https://github.com/fluencelabs/redis
synced 2025-06-30 09:21:34 +00:00
master branch merged into scripting.
This commit is contained in:
67
src/Makefile
67
src/Makefile
@ -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);
|
||||
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
|
||||
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
|
||||
@ -86,38 +86,37 @@ ae_kqueue.o: ae_kqueue.c
|
||||
ae_select.o: ae_select.c
|
||||
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 \
|
||||
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 \
|
||||
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.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
|
||||
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
|
||||
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
|
||||
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
|
||||
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 \
|
||||
slowlog.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 slowlog.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 slowlog.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 slowlog.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 slowlog.h \
|
||||
sha1.h
|
||||
dscache.o: dscache.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
|
||||
dict.o: dict.c fmacros.h dict.h zmalloc.h
|
||||
endian.o: endian.c
|
||||
intset.o: intset.c intset.h zmalloc.h endian.h
|
||||
lzf_c.o: lzf_c.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 \
|
||||
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 \
|
||||
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 \
|
||||
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
|
||||
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 \
|
||||
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 \
|
||||
../deps/hiredis/hiredis.h sds.h adlist.h zmalloc.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
|
||||
redis-cli.o: redis-cli.c fmacros.h version.h ../deps/hiredis/hiredis.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 \
|
||||
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
|
||||
release.o: release.c release.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
|
||||
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 \
|
||||
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 \
|
||||
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 \
|
||||
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 \
|
||||
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 \
|
||||
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 \
|
||||
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 \
|
||||
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
|
||||
ziplist.o: ziplist.c zmalloc.h util.h ziplist.h endian.h
|
||||
zipmap.o: zipmap.c zmalloc.h endian.h
|
||||
@ -197,8 +202,8 @@ clean:
|
||||
dep:
|
||||
$(CC) -MM *.c -I ../deps/hiredis -I ../deps/linenoise
|
||||
|
||||
test: redis-server
|
||||
@(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.")
|
||||
test: redis-server redis-check-aof
|
||||
@(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:
|
||||
./redis-benchmark
|
||||
|
@ -287,6 +287,8 @@ int loadAppendOnlyFile(char *filename) {
|
||||
|
||||
/* The fake client should not have a reply */
|
||||
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
|
||||
* argv/argc of the client instead of the local variables. */
|
||||
|
20
src/config.c
20
src/config.c
@ -298,6 +298,12 @@ void loadServerConfig(char *filename) {
|
||||
server.cluster.configfile = zstrdup(argv[1]);
|
||||
} else if (!strcasecmp(argv[0],"lua-time-limit") && argc == 2) {
|
||||
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 {
|
||||
err = "Bad directive or wrong number of arguments"; goto loaderr;
|
||||
}
|
||||
@ -471,6 +477,12 @@ void configSetCommand(redisClient *c) {
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"lua-time-limit")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
|
||||
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 {
|
||||
addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
|
||||
(char*)c->argv[2]->ptr);
|
||||
@ -645,6 +657,14 @@ void configGetCommand(redisClient *c) {
|
||||
if (stringmatch(pattern,"lua-time-limit",0)) {
|
||||
addReplyBulkCString(c,"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++;
|
||||
}
|
||||
setDeferredMultiBulkLength(c,replylen,matches*2);
|
||||
|
23
src/db.c
23
src/db.c
@ -476,6 +476,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.
|
||||
@ -513,10 +516,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);
|
||||
signalModifiedKey(c->db,key);
|
||||
addReply(c, shared.cone);
|
||||
return;
|
||||
} else {
|
||||
time_t when = time(NULL)+seconds;
|
||||
|
@ -281,6 +281,12 @@ void debugCommand(redisClient *c) {
|
||||
d = sdscatprintf(d, "%02x",digest[j]);
|
||||
addReplyStatus(c,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 {
|
||||
addReplyError(c,
|
||||
"Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|SWAPIN <key>|SWAPOUT <key>|RELOAD]");
|
||||
|
11
src/multi.c
11
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);
|
||||
|
@ -36,6 +36,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;
|
||||
@ -461,6 +462,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) {
|
||||
@ -961,5 +963,7 @@ void rewriteClientCommandVector(redisClient *c, int argc, ...) {
|
||||
/* Replace argv and argc with our new versions. */
|
||||
c->argv = argv;
|
||||
c->argc = argc;
|
||||
c->cmd = lookupCommand(c->argv[0]->ptr);
|
||||
redisAssert(c->cmd != NULL);
|
||||
va_end(ap);
|
||||
}
|
||||
|
86
src/redis.c
86
src/redis.c
@ -28,6 +28,7 @@
|
||||
*/
|
||||
|
||||
#include "redis.h"
|
||||
#include "slowlog.h"
|
||||
|
||||
#ifdef HAVE_BACKTRACE
|
||||
#include <execinfo.h>
|
||||
@ -194,7 +195,8 @@ struct redisCommand redisCommandTable[] = {
|
||||
{"object",objectCommand,-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},
|
||||
{"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 ============================ */
|
||||
@ -871,6 +873,10 @@ void initServerConfig() {
|
||||
populateCommandTable();
|
||||
server.delCommand = lookupCommandByCString("del");
|
||||
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() {
|
||||
@ -958,6 +964,7 @@ void initServer() {
|
||||
|
||||
if (server.cluster_enabled) clusterInit();
|
||||
scriptingInit();
|
||||
slowlogInit();
|
||||
srand(time(NULL)^getpid());
|
||||
}
|
||||
|
||||
@ -1004,18 +1011,20 @@ struct redisCommand *lookupCommandByCString(char *s) {
|
||||
}
|
||||
|
||||
/* Call() is the core of Redis execution of a command */
|
||||
void call(redisClient *c, struct redisCommand *cmd) {
|
||||
long long dirty, start = ustime();
|
||||
void call(redisClient *c) {
|
||||
long long dirty, start = ustime(), duration;
|
||||
|
||||
dirty = server.dirty;
|
||||
cmd->proc(c);
|
||||
c->cmd->proc(c);
|
||||
dirty = server.dirty-dirty;
|
||||
cmd->microseconds += ustime()-start;
|
||||
cmd->calls++;
|
||||
duration = ustime()-start;
|
||||
c->cmd->microseconds += duration;
|
||||
slowlogPushEntryIfNeeded(c->argv,c->argc,duration);
|
||||
c->cmd->calls++;
|
||||
|
||||
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))
|
||||
@ -1032,8 +1041,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
|
||||
@ -1045,28 +1052,29 @@ 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;
|
||||
}
|
||||
|
||||
/* If cluster is enabled, redirect here */
|
||||
if (server.cluster_enabled &&
|
||||
!(cmd->getkeys_proc == NULL && cmd->firstkey == 0)) {
|
||||
!(c->cmd->getkeys_proc == NULL && c->cmd->firstkey == 0)) {
|
||||
int hashslot;
|
||||
|
||||
if (server.cluster.state != REDIS_CLUSTER_OK) {
|
||||
@ -1074,7 +1082,7 @@ int processCommand(redisClient *c) {
|
||||
return REDIS_OK;
|
||||
} else {
|
||||
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) {
|
||||
addReplyError(c,"Multi keys request invalid in cluster");
|
||||
return REDIS_OK;
|
||||
@ -1093,7 +1101,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'");
|
||||
@ -1103,8 +1111,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;
|
||||
}
|
||||
@ -1113,7 +1123,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");
|
||||
@ -1121,20 +1131,20 @@ 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 {
|
||||
call(c,cmd);
|
||||
call(c);
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
@ -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 */
|
||||
if (allsections || defsections || !strcasecmp(section,"persistence")) {
|
||||
if (sections++) info = sdscat(info,"\r\n");
|
||||
@ -1456,8 +1450,8 @@ sds genRedisInfoString(char *section) {
|
||||
"# CPU\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",
|
||||
(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)c_ru.ru_utime.tv_sec+(float)c_ru.ru_utime.tv_usec/1000000,
|
||||
|
29
src/redis.h
29
src/redis.h
@ -21,17 +21,17 @@
|
||||
#include <netinet/in.h>
|
||||
#include <lua.h>
|
||||
|
||||
#include "ae.h" /* Event driven programming library */
|
||||
#include "sds.h" /* Dynamic safe strings */
|
||||
#include "dict.h" /* Hash tables */
|
||||
#include "adlist.h" /* Linked lists */
|
||||
#include "ae.h" /* Event driven programming library */
|
||||
#include "sds.h" /* Dynamic safe strings */
|
||||
#include "dict.h" /* Hash tables */
|
||||
#include "adlist.h" /* Linked lists */
|
||||
#include "zmalloc.h" /* total memory usage aware version of malloc/free */
|
||||
#include "anet.h" /* Networking the easy way */
|
||||
#include "zipmap.h" /* Compact string -> string data structure */
|
||||
#include "anet.h" /* Networking the easy way */
|
||||
#include "zipmap.h" /* Compact string -> string data structure */
|
||||
#include "ziplist.h" /* Compact list data structure */
|
||||
#include "intset.h" /* Compact integer set structure */
|
||||
#include "version.h"
|
||||
#include "util.h"
|
||||
#include "intset.h" /* Compact integer set structure */
|
||||
#include "version.h" /* Version macro */
|
||||
#include "util.h" /* Misc functions useful in many places */
|
||||
|
||||
/* Error codes */
|
||||
#define REDIS_OK 0
|
||||
@ -53,6 +53,8 @@
|
||||
#define REDIS_MAX_LOGMSG_LEN 1024 /* Default maximum length of syslog messages */
|
||||
#define REDIS_AUTO_AOFREWRITE_PERC 100
|
||||
#define REDIS_AUTO_AOFREWRITE_MIN_SIZE (1024*1024)
|
||||
#define REDIS_SLOWLOG_LOG_SLOWER_THAN 10000
|
||||
#define REDIS_SLOWLOG_MAX_LEN 64
|
||||
|
||||
/* Hash table parameters */
|
||||
#define REDIS_HT_MINFILL 10 /* Minimal hash table fill 10% */
|
||||
@ -312,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 */
|
||||
@ -530,6 +533,10 @@ struct redisServer {
|
||||
long long stat_keyspace_misses; /* number of failed lookups of keys */
|
||||
size_t stat_peak_memory; /* max used memory record */
|
||||
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 */
|
||||
int verbosity;
|
||||
int maxidletime;
|
||||
@ -807,7 +814,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);
|
||||
|
||||
@ -918,7 +925,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 redisLogRaw(int level, const char *msg);
|
||||
|
115
src/slowlog.c
Normal file
115
src/slowlog.c
Normal file
@ -0,0 +1,115 @@
|
||||
#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.");
|
||||
}
|
||||
}
|
15
src/slowlog.h
Normal file
15
src/slowlog.h
Normal file
@ -0,0 +1,15 @@
|
||||
/* 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) {
|
||||
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;
|
||||
|
||||
@ -917,6 +918,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;
|
||||
@ -934,6 +936,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;
|
||||
}
|
||||
|
@ -3,3 +3,15 @@
|
||||
Memcheck:Cond
|
||||
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 @@
|
||||
|
||||
#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)
|
||||
|
||||
@ -88,8 +85,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",
|
||||
@ -190,11 +185,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;
|
||||
}
|
||||
|
@ -75,8 +75,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 */
|
||||
|
Reference in New Issue
Block a user