Compare commits

...
42 Commits
Author SHA1 Message Date
antirez 8ff874d2b2 2.2.15 release notes 2011-10-17 10:57:18 +02:00
antirez f97b01d36a version bumped to 2.2.15 2011-10-17 10:56:12 +02:00
antirez 991eee4d01 FLUSHALL now prevents rdbSave() from resetting the dirty counter, so that the command will get replicated and put inside the AOF. This fixes issue #142 2011-10-17 10:55:46 +02:00
antirez 74b77ff8a6 FLUSHALL will only perform a blocking SAVE if RDB persistence is configured. 2011-10-17 10:55:37 +02:00
antirez 15da88c8dc Compilation fixed on OpenBSD making sure that _XOPEN_SOURCE is set to 700 in fmacros.h 2011-09-29 10:20:36 +02:00
Juri M. Vainonenandantirez ee2f20dfde fixed a small bug that caused redis-cli to segfault when given single numeric parameter greater that zero. 2011-09-21 22:33:13 +02:00
antirez 0dfc5578bf Version bumped to 2.2.14, Changelog updated. 2011-09-21 18:12:56 +02:00
antirez ee9f3679a9 Fixed a bug in the average latency metering of redis-cli --latency 2011-09-21 18:10:07 +02:00
antirez 28de624c70 Implemented --latency in redis-cli 2011-09-21 18:09:46 +02:00
Pieter Noordhuisandantirez 5d4675979d Fix AOF race that may duplicate commands 2011-09-16 11:22:29 +02:00
antirez 9e087a298d Optimize LRANGE to scan the list starting from the head or the tail in order to traverse the minimal number of elements. Thanks to Didier Spezia for noticing the problem and providing a patch. 2011-09-14 15:17:09 +02:00
antirez 8ac3c86664 Don't replicate/AOF SAVE 2011-09-14 14:54:08 +02:00
antirez a8ed663376 Version bumped to 2.2.13, changelog updated. 2011-09-12 15:49:20 +02:00
antirez 61f57b6a8f Remove the write handler only if there are no longer objects in the output queue AND if the static buffer is empty. This bug was the cause of a possible server-stop-responding-to-client bug under some specific work load. Thanks to Pieter Noordhuis for spotting and fixing it. 2011-09-12 11:05:48 +02:00
antirez 24a1580df5 Fix for 2.2 TCMALLOC build, thanks to Pieter Noordhuis for the patch. 2011-09-12 11:01:55 +02:00
antirez 4793a2c3c3 fixed typos in the comments of rpoplpushHandlePush() 2011-09-12 10:06:00 +02:00
Hampus Wessmanandantirez 1d05b53fcc Fix crash when chaining brpoplpush with other blocking commands. 2011-09-12 10:05:58 +02:00
Pieter Noordhuis e1f01c9b28 Fix link order for ld --as-needed (thanks to Nigel Babu) 2011-07-28 10:42:57 +02:00
antirez c1b270127c Merge remote-tracking branch 'origin/2.2' into 2.2 2011-07-27 16:37:45 +02:00
Pieter Noordhuisandantirez 62e6f6c29a Fix adding bulk reply when getcwd fails 2011-07-27 16:22:58 +02:00
antirez 5960ac9dec Redis 2.2.12 2011-07-22 12:22:26 +02:00
antirez ec7bbadce9 Close the listening sockets. Apparently this allows faster restarts. 2011-07-22 12:12:55 +02:00
antirez 063923de1c prepareForShutdown() fixed for correctness regarding what to do with AOF and RDB persistence on exit. 2011-07-22 12:03:24 +02:00
Hampus Wessmanandantirez aa96ebec89 Add test for incorrect expiration when loading AOF. 2011-07-15 18:15:27 +02:00
antirez 563f4cfee5 force VM usage when doing the VM test in 2.2, adding the really-use-vm config option. 2011-07-15 18:13:43 +02:00
antirez e8108591e4 Warn the user that will try to enable VM that VM sucks. But still allows him to enable VM with a special option. 2011-07-15 18:11:00 +02:00
antirez eed4ec4664 Warn the user in redis.conf that VM is deprecated and discouraged 2011-07-15 18:10:20 +02:00
antirez 9702c92c4f Fixed another merge typo 2011-07-15 18:04:41 +02:00
antirez 3fee7e3013 removed a second copy of rewriteClientCommandVector put inside the source code for a merge error 2011-07-15 18:02:45 +02:00
antirez 891f9196fc Manual merge after cherry-pick 2011-07-15 18:00:24 +02:00
antirez 59aecb3a6c Take a pointer to the relevant entry of the command table in the client structure. This is generally a more sounding design, simplifies a few functions prototype, and as a side effect fixes a bug related to the conversion of EXPIRE -1 to DEL: before of this fix Redis tried to convert it into an EXPIREAT in the AOF code, regardless of our rewrite of the command. 2011-07-15 18:00:03 +02:00
antirez b8082ae7ba don't process EXPIRE with negative TTL or EXPIREAT with time in the past if we are a slave too (see http://groups.google.com/group/redis-db/browse_thread/thread/5a931fefb88b16d5). Also propagate it as DEL. 2011-07-15 17:59:27 +02:00
antirez d30dafe7f4 use the new rewriteClientCommandVector() function for SPOP -> SREM replication translation as well. 2011-07-15 17:58:22 +02:00
Hampus Wessmanandantirez a906670e2d Don't expire keys while loading AOF.
They will be expired (and a DEL will be logged) after the loading is done
instead.
2011-07-15 17:48:22 +02:00
antirez d3203c16f5 Fixed a problem with AOF when it is stopped via CONFIG SET appendonly no. We used to kill the wrong child... 2011-07-07 13:24:44 +02:00
antirez 2e1adff83e Fixed typo in redis.conf related to slow log parameters 2011-07-03 15:50:21 +02:00
antirez 194058931f childrens -> children in INFO output field names. This is a backward incompatible change, but really a minimal one. 2011-07-02 10:52:41 +02:00
antirez 5a7bfb8a84 colorized make for 2.2 as well 2011-07-02 10:50:13 +02:00
antirez aaa7fe1551 no more allocation stats info in INFO, useless now that we have jemalloc. 2011-07-02 10:33:52 +02:00
antirez 33d2761bc0 ustime() backported from unstable, used by slow log 2011-07-01 15:47:48 +02:00
antirez ad6347b74e Slowlog backported to 2.2 2011-07-01 15:45:36 +02:00
antirez d3b5989148 Partial fix for a problem with AOF and BRPOP. We have at least a single evidence of an AOF that ended with a BRPOP call against an empty list, causing later issues at run time. Now the problem is detected while loading the AOF at startup, even if it is still to understand how the corrupted AOF was generated. 2011-06-29 16:10:28 +02:00
25 changed files with 548 additions and 118 deletions
+39
View File
@@ -12,6 +12,45 @@ for 2.0.
CHANGELOG
---------
What's new in Redis 2.2.15
==========================
* [BUGFIX] FLUSHALL was not replicated nor written into the Append Only File.
* [BUGFIX] FLUSHALL now only performs a sync SAVE if there is at least
one save point configured.
What's new in Redis 2.2.14
==========================
* [BUGFIX] Fixed a rare but possible AOF race condition that could result into
duplicated commands inside the AOF.
* [BUGFIX] Don't replicate SAVE.
* LRANGE optimization may drastically improve performances when querying the
final part of a long list.
* redis-cli now implements a --latency mode to monitory Redis delay.
What's new in Redis 2.2.13
==========================
* [BUGFIX] Fixed issue 593 (BRPOPLPUSH related crash).
* [BUGFIX] Fixed an issue with the networking layer that may prevent Redis from sending the whole reply back to client under extreme conditions.
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
==========================
+3 -3
View File
@@ -50,7 +50,7 @@ typedef struct redisReader {
size_t pos; /* buffer cursor */
size_t len; /* buffer length */
redisReadTask rstack[3]; /* stack of read tasks */
redisReadTask rstack[9]; /* stack of read tasks */
int ridx; /* index of stack */
void *privdata; /* user-settable arbitrary field */
} redisReader;
@@ -340,9 +340,9 @@ static int processMultiBulkItem(redisReader *r) {
int root = 0;
/* Set error for nested multi bulks with depth > 1 */
if (r->ridx == 2) {
if (r->ridx == 8) {
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;
}
+27
View File
@@ -292,8 +292,35 @@ appendfsync everysec
# "no" that is the safest pick from the point of view of durability.
no-appendfsync-on-rewrite no
################################## 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
################################ 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
+39 -19
View File
@@ -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
@@ -19,13 +32,13 @@ ifeq ($(USE_TCMALLOC),yes)
CCLINK+= -ltcmalloc
CFLAGS+= -DUSE_TCMALLOC
endif
CCOPT= $(CFLAGS) $(CCLINK) $(ARCH) $(PROF)
CCOPT= $(CFLAGS) $(ARCH) $(PROF)
PREFIX= /usr/local
INSTALL_BIN= $(PREFIX)/bin
INSTALL= cp -p
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 vm.o pubsub.o multi.o debug.o sort.o intset.o syncio.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 vm.o pubsub.o multi.o debug.o sort.o intset.o syncio.o slowlog.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
@@ -51,7 +64,6 @@ 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
chprgname.o: chprgname.c
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
db.o: db.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
@@ -73,21 +85,26 @@ 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
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 lzf.h
redis-benchmark.o: redis-benchmark.c fmacros.h ae.h anet.h sds.h adlist.h \
zmalloc.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
redis-check-dump.o: redis-check-dump.c lzf.h
redis-cli.o: redis-cli.c fmacros.h version.h sds.h adlist.h zmalloc.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
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h slowlog.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
sds.o: sds.c sds.h zmalloc.h
sha1.o: sha1.c sha1.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 \
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 pqsort.h
syncio.o: syncio.c
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
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
t_list.o: t_list.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
@@ -104,42 +121,45 @@ vm.o: vm.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
ziplist.o: ziplist.c zmalloc.h ziplist.h
zipmap.o: zipmap.c zmalloc.h
zmalloc.o: zmalloc.c config.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) $(CCLINK)
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 $(CCLINK)
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 $(CCLINK)
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) $(CCLINK)
redis-check-aof: $(CHECKAOFOBJ)
$(CC) -o $(CHECKAOFPRGNAME) $(CCOPT) $(DEBUG) $(CHECKAOFOBJ)
$(QUIET_LINK)$(CC) -o $(CHECKAOFPRGNAME) $(CCOPT) $(DEBUG) $(CHECKAOFOBJ) $(CCLINK)
.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
dep:
$(CC) -MM *.c
$(CC) -MM *.c -I ../deps/hiredis -I ../deps/linenoise
test: redis-server
(cd ..; tclsh8.5 tests/test_helper.tcl --tags "${TAGS}" --file "${FILE}")
+10 -3
View File
@@ -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;
}
}
@@ -284,6 +284,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. */
@@ -655,6 +657,11 @@ void backgroundRewriteDoneHandler(int statloc) {
if (server.appendfsync != APPENDFSYNC_NO) aof_fsync(fd);
server.appendseldb = -1; /* Make sure it will issue SELECT */
redisLog(REDIS_NOTICE,"The new append only file was selected for future appends.");
/* Clear regular AOF buffer since its contents was just written to
* the new AOF from the background rewrite buffer. */
sdsfree(server.aofbuf);
server.aofbuf = sdsempty();
} else {
/* If append only is disabled we just generate a dump in this
* format. Why not? */
+41 -5
View File
@@ -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]);
@@ -288,6 +293,12 @@ void loadServerConfig(char *filename) {
err = "Target command name already exists"; goto loaderr;
}
}
} 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;
}
@@ -297,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:
@@ -305,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);
}
/*-----------------------------------------------------------------------------
@@ -446,6 +467,12 @@ void configSetCommand(redisClient *c) {
} else if (!strcasecmp(c->argv[2]->ptr,"set-max-intset-entries")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.set_max_intset_entries = 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);
@@ -471,12 +498,11 @@ void configGetCommand(redisClient *c) {
if (stringmatch(pattern,"dir",0)) {
char buf[1024];
addReplyBulkCString(c,"dir");
if (getcwd(buf,sizeof(buf)) == NULL) {
if (getcwd(buf,sizeof(buf)) == NULL)
buf[0] = '\0';
} else {
addReplyBulkCString(c,buf);
}
addReplyBulkCString(c,"dir");
addReplyBulkCString(c,buf);
matches++;
}
if (stringmatch(pattern,"dbfilename",0)) {
@@ -597,6 +623,16 @@ void configGetCommand(redisClient *c) {
addReplyBulkLongLong(c,server.set_max_intset_entries);
matches++;
}
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);
}
+27 -4
View File
@@ -180,7 +180,13 @@ void flushallCommand(redisClient *c) {
kill(server.bgsavechildpid,SIGKILL);
rdbRemoveTempFile(server.bgsavechildpid);
}
rdbSave(server.dbfilename);
if (server.saveparamslen > 0) {
/* Normally rdbSave() will reset dirty, but we don't want this here
* as otherwise FLUSHALL will not be replicated nor put into the AOF. */
int saved_dirty = server.dirty;
rdbSave(server.dbfilename);
server.dirty = saved_dirty;
}
server.dirty++;
}
@@ -455,6 +461,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 +501,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;
+6
View File
@@ -289,6 +289,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]");
+1 -1
View File
@@ -3,7 +3,7 @@
#define _BSD_SOURCE
#ifdef __linux__
#if defined(__linux__) || defined(__OpenBSD__)
#define _XOPEN_SOURCE 700
#else
#define _XOPEN_SOURCE
+8 -3
View File
@@ -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);
+3 -2
View File
@@ -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) {
@@ -609,7 +611,7 @@ void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
}
}
if (totwritten > 0) c->lastinteraction = time(NULL);
if (listLength(c->reply) == 0) {
if (c->bufpos == 0 && listLength(c->reply) == 0) {
c->sentlen = 0;
aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
@@ -896,4 +898,3 @@ void rewriteClientCommandVector(redisClient *c, int argc, ...) {
c->argc = argc;
va_end(ap);
}
+44 -1
View File
@@ -61,6 +61,7 @@ static struct config {
int shutdown;
int monitor_mode;
int pubsub_mode;
int latency_mode;
int stdinarg; /* get last arg from stdin. (-x option) */
char *auth;
int raw_output; /* output mode per command */
@@ -564,6 +565,8 @@ static int parseOptions(int argc, char **argv) {
i++;
} else if (!strcmp(argv[i],"--raw")) {
config.raw_output = 1;
} else if (!strcmp(argv[i],"--latency")) {
config.latency_mode = 1;
} else if (!strcmp(argv[i],"-d") && !lastarg) {
sdsfree(config.mb_delim);
config.mb_delim = sdsnew(argv[i+1]);
@@ -614,6 +617,7 @@ static void usage() {
" -x Read last argument from STDIN\n"
" -d <delimiter> Multi-bulk delimiter in for raw formatting (default: \\n)\n"
" --raw Use raw formatting for replies (default when STDOUT is not a tty)\n"
" --latency Enter a special mode continuously sampling latency.\n"
" --help Output this help and exit\n"
" --version Output version and exit\n"
"\n"
@@ -690,7 +694,7 @@ static void repl() {
int repeat, skipargs = 0;
repeat = atoi(argv[0]);
if (repeat) {
if (argc > 1 && repeat) {
skipargs = 1;
} else {
repeat = 1;
@@ -736,6 +740,38 @@ static int noninteractive(int argc, char **argv) {
return retval;
}
static void latencyMode(void) {
redisReply *reply;
long long start, latency, min, max, tot, count = 0;
double avg;
if (!context) exit(1);
while(1) {
start = mstime();
reply = redisCommand(context,"PING");
if (reply == NULL) {
fprintf(stderr,"\nI/O error\n");
exit(1);
}
latency = mstime()-start;
freeReplyObject(reply);
count++;
if (count == 1) {
min = max = tot = latency;
avg = (double) latency;
} else {
if (latency < min) min = latency;
if (latency > max) max = latency;
tot += latency;
avg = (double) tot/count;
}
printf("\x1b[0G\x1b[2Kmin: %lld, max: %lld, avg: %.2f (%lld samples)",
min, max, avg, count);
fflush(stdout);
usleep(10000);
}
}
int main(int argc, char **argv) {
int firstarg;
@@ -749,6 +785,7 @@ int main(int argc, char **argv) {
config.shutdown = 0;
config.monitor_mode = 0;
config.pubsub_mode = 0;
config.latency_mode = 0;
config.stdinarg = 0;
config.auth = NULL;
config.raw_output = !isatty(fileno(stdout)) && (getenv("FAKETTY") == NULL);
@@ -759,6 +796,12 @@ int main(int argc, char **argv) {
argc -= firstarg;
argv += firstarg;
/* Start in latency mode if appropriate */
if (config.latency_mode) {
cliConnect(0);
latencyMode();
}
/* Start interactive mode when no command is provided */
if (argc == 0) {
/* Note that in repl mode we don't abort on connection error.
+66 -50
View File
@@ -28,6 +28,7 @@
*/
#include "redis.h"
#include "slowlog.h"
#ifdef HAVE_BACKTRACE
#include <execinfo.h>
@@ -188,7 +189,8 @@ struct redisCommand readonlyCommandTable[] = {
{"publish",publishCommand,3,REDIS_CMD_FORCE_REPLICATION,NULL,0,0,0},
{"watch",watchCommand,-2,0,NULL,0,0,0},
{"unwatch",unwatchCommand,1,0,NULL,0,0,0},
{"object",objectCommand,-2,0,NULL,0,0,0}
{"object",objectCommand,-2,0,NULL,0,0,0},
{"slowlog",slowlogCommand,-2,0,NULL,0,0,0}
};
/*============================ Utility functions ============================ */
@@ -675,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)
@@ -831,6 +833,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() {
@@ -917,6 +923,7 @@ void initServer() {
}
if (server.vm_enabled) vmInit();
slowlogInit();
srand(time(NULL)^getpid());
}
@@ -951,16 +958,18 @@ 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;
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) &&
if (server.appendonly && dirty > 0)
feedAppendOnlyFile(c->cmd,c->db->id,c->argv,c->argc);
if ((dirty > 0 || c->cmd->flags & REDIS_CMD_FORCE_REPLICATION) &&
listLength(server.slaves))
replicationFeedSlaves(server.slaves,c->db->id,c->argv,c->argc);
if (listLength(server.monitors))
@@ -977,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
@@ -990,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;
}
@@ -1015,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'");
@@ -1025,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;
}
@@ -1035,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");
@@ -1043,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;
}
@@ -1066,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
@@ -1087,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;
}
@@ -1167,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"
@@ -1326,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;
+11 -3
View File
@@ -49,6 +49,8 @@
#define REDIS_SHARED_INTEGERS 10000
#define REDIS_REPLY_CHUNK_BYTES (5*1500) /* 5 TCP packets with default MTU */
#define REDIS_MAX_LOGMSG_LEN 1024 /* Default maximum length of syslog messages */
#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 */
@@ -388,6 +391,10 @@ struct redisServer {
long long stat_evictedkeys; /* number of evicted keys (maxmemory) */
long long stat_keyspace_hits; /* number of successful lookups of keys */
long long stat_keyspace_misses; /* number of failed lookups of keys */
list *slowlog;
long long slowlog_entry_id;
long long slowlog_log_slower_than;
unsigned long slowlog_max_len;
/* Configuration */
int verbosity;
int maxidletime;
@@ -694,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);
@@ -782,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();
@@ -813,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);
@@ -863,6 +870,7 @@ int ll2string(char *s, size_t len, long long value);
int isStringRepresentableAsLong(sds s, long *longval);
int isStringRepresentableAsLongLong(sds s, long long *longval);
int isObjectRepresentableAsLongLong(robj *o, long long *llongval);
long long ustime(void);
/* Configuration */
void loadServerConfig(char *filename);
+115
View 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
View 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);
+15 -5
View File
@@ -514,7 +514,12 @@ void lrangeCommand(redisClient *c) {
p = ziplistNext(o->ptr,p);
}
} else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {
listNode *ln = listIndex(o->ptr,start);
listNode *ln;
/* If we are nearest to the end of the list, reach the element
* starting from tail and going backward, as it is faster. */
if (start > llen/2) start -= llen;
ln = listIndex(o->ptr,start);
while(rangelen--) {
addReplyBulk(c,ln->value);
@@ -638,7 +643,7 @@ void lremCommand(redisClient *c) {
void rpoplpushHandlePush(redisClient *origclient, redisClient *c, robj *dstkey, robj *dstobj, robj *value) {
robj *aux;
if (!handleClientsWaitingListPush(c,dstkey,value)) {
if (!handleClientsWaitingListPush(origclient,dstkey,value)) {
/* Create the list if the key does not exist */
if (!dstobj) {
dstobj = createZiplistObject();
@@ -648,10 +653,12 @@ void rpoplpushHandlePush(redisClient *origclient, redisClient *c, robj *dstkey,
}
listTypePush(dstobj,value,REDIS_HEAD);
/* If we are pushing as a result of LPUSH against a key
* watched by BLPOPLPUSH, we need to rewrite the command vector.
* But if this is called directly by RPOPLPUSH (either directly
* watched by BRPOPLPUSH, we need to rewrite the command vector
* as an LPUSH.
*
* If this is called directly by RPOPLPUSH (either directly
* or via a BRPOPLPUSH where the popped list exists)
* we should replicate the BRPOPLPUSH command itself. */
* we should replicate the RPOPLPUSH command itself. */
if (c != origclient) {
aux = createStringObject("LPUSH",5);
rewriteClientCommandVector(origclient,3,aux,dstkey,value);
@@ -904,6 +911,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 +919,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 +937,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;
}
+12
View File
@@ -1,6 +1,7 @@
#include "redis.h"
#include <ctype.h>
#include <limits.h>
#include <sys/time.h>
/* Glob-style pattern matching. */
int stringmatchlen(const char *pattern, int patternLen,
@@ -241,3 +242,14 @@ int isObjectRepresentableAsLongLong(robj *o, long long *llongval) {
return isStringRepresentableAsLongLong(o->ptr,llongval);
}
}
/* Return the UNIX time in microseconds */
long long ustime(void) {
struct timeval tv;
long long ust;
gettimeofday(&tv, NULL);
ust = ((long long)tv.tv_sec)*1000000;
ust += tv.tv_usec;
return ust;
}
+1 -1
View File
@@ -1 +1 @@
#define REDIS_VERSION "2.2.11"
#define REDIS_VERSION "2.2.15"
+4 -4
View File
@@ -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. */
-10
View File
@@ -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;
}
-3
View File
@@ -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 */
+18
View File
@@ -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]
}
}
}
+2 -1
View File
@@ -125,9 +125,10 @@ proc execute_everything {} {
execute_tests "integration/aof"
# execute_tests "integration/redis-cli"
execute_tests "unit/pubsub"
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"
+41
View File
@@ -0,0 +1,41 @@
start_server {tags {"slowlog"}} {
test {SLOWLOG - check that it starts with an empty log} {
r slowlog len
} {0}
test {SLOWLOG - only logs commands taking more time than specified} {
r config set slowlog-log-slower-than 100000
r ping
assert_equal [r slowlog len] 0
r debug sleep 0.2
assert_equal [r slowlog len] 1
}
test {SLOWLOG - max entries is correctly handled} {
r config set slowlog-log-slower-than 0
r config set slowlog-max-len 10
for {set i 0} {$i < 100} {incr i} {
r ping
}
r slowlog len
} {10}
test {SLOWLOG - GET optional argument to limit output len works} {
llength [r slowlog get 5]
} {5}
test {SLOWLOG - RESET subcommand works} {
r config set slowlog-log-slower-than 100000
r slowlog reset
r slowlog len
} {0}
test {SLOWLOG - logged entry sanity check} {
r debug sleep 0.2
set e [lindex [r slowlog get] 0]
assert_equal [llength $e] 4
assert_equal [lindex $e 0] 105
assert_equal [expr {[lindex $e 2] > 100000}] 1
assert_equal [lindex $e 3] {debug sleep 0.2}
}
}