]> git.saurik.com Git - redis.git/commitdiff
Merge pull request #414 from chobits/unstable
authorSalvatore Sanfilippo <antirez@gmail.com>
Sat, 21 Apr 2012 10:27:15 +0000 (03:27 -0700)
committerSalvatore Sanfilippo <antirez@gmail.com>
Sat, 21 Apr 2012 10:27:15 +0000 (03:27 -0700)
fix typos in tests/assets/default.conf

53 files changed:
README
deps/Makefile
deps/linenoise/Makefile
redis.conf
src/.gitignore [new file with mode: 0644]
src/Makefile
src/Makefile.dep [new file with mode: 0644]
src/ae.c
src/anet.c
src/aof.c
src/cluster.c
src/config.c
src/config.h
src/crc64.c [new file with mode: 0644]
src/db.c
src/debug.c
src/dict.c
src/help.h
src/memtest.c
src/networking.c
src/object.c
src/rdb.c
src/rdb.h
src/redis-cli.c
src/redis-trib.rb
src/redis.c
src/redis.h
src/replication.c
src/rio.c
src/rio.h
src/scripting.c
src/sort.c
src/syncio.c
src/t_list.c
src/version.h
src/zmalloc.h
tests/helpers/bg_complex_data.tcl [new file with mode: 0644]
tests/integration/replication-4.tcl [new file with mode: 0644]
tests/integration/replication.tcl
tests/support/redis.tcl
tests/support/server.tcl
tests/test_helper.tcl
tests/unit/aofrw.tcl
tests/unit/dump.tcl [new file with mode: 0644]
tests/unit/introspection.tcl
tests/unit/limits.tcl [new file with mode: 0644]
tests/unit/scripting.tcl
tests/unit/sort.tcl
tests/unit/type/hash.tcl
tests/unit/type/list.tcl
tests/unit/type/set.tcl
utils/generate-command-help.rb
utils/install_server.sh

diff --git a/README b/README
index bba2439c339b58b76c958bd63d30d6d61d3d7416..1c3f574678d24e30201d0bbabd70acd6085320d2 100644 (file)
--- a/README
+++ b/README
@@ -7,6 +7,13 @@ documentation at http://redis.io
 Building Redis
 --------------
 
+Redis can be compiled and used on Linux, OSX, OpenBSD, NetBSD, FreeBSD.
+We support big endian and little endian architectures.
+
+It may compile on Solaris derived systems (for instance SmartOS) but our
+support for this platform is "best effort" and Redis is not guaranteed to
+work as well as in Linux, OSX, and *BSD there.
+
 It is as simple as:
 
     % make
index b881c814e7dbab036fe5fecb17057f460cf5194b..7cd9c0f64451bcc0262bf606627b76f969d0ad4c 100644 (file)
@@ -1,17 +1,6 @@
 # Redis dependency Makefile
 
-UNAME_S:=$(shell sh -c 'uname -s 2> /dev/null || echo not')
-
-LUA_CFLAGS=-O2 -Wall $(ARCH)
-ifeq ($(UNAME_S),SunOS)
-  # Make isinf() available
-  LUA_CFLAGS+= -D__C99FEATURES__=1
-endif
-
-JEMALLOC_CFLAGS=
-ifeq ($(ARCH),-m32)
-  JEMALLOC_CFLAGS+=CFLAGS="-std=gnu99 -Wall -pipe -g3 -fvisibility=hidden -O3 -funroll-loops -m32"
-endif
+uname_S:= $(shell sh -c 'uname -s 2>/dev/null || echo not')
 
 CCCOLOR="\033[34m"
 LINKCOLOR="\033[34;1m"
@@ -23,37 +12,67 @@ ENDCOLOR="\033[0m"
 default:
        @echo "Explicit target required"
 
-# Clean everything when ARCH is different
-ifneq ($(shell sh -c '[ -f .make-arch ] && cat .make-arch'), $(ARCH))
-.make-arch: distclean
-else
-.make-arch:
+.PHONY: default
+
+# Prerequisites target
+.make-prerequisites:
+       @touch $@
+
+# Clean everything when CFLAGS is different
+ifneq ($(shell sh -c '[ -f .make-cflags ] && cat .make-cflags || echo none'), $(CFLAGS))
+.make-cflags: distclean
+       -(echo "$(CFLAGS)" > .make-cflags)
+.make-prerequisites: .make-cflags
 endif
 
-.make-arch:
-       -(echo $(ARCH) > .make-arch)
+# Clean everything when LDFLAGS is different
+ifneq ($(shell sh -c '[ -f .make-ldflags ] && cat .make-ldflags || echo none'), $(LDFLAGS))
+.make-ldflags: distclean
+       -(echo "$(LDFLAGS)" > .make-ldflags)
+.make-prerequisites: .make-ldflags
+endif
 
 distclean:
        -(cd hiredis && $(MAKE) clean) > /dev/null || true
        -(cd linenoise && $(MAKE) clean) > /dev/null || true
        -(cd lua && $(MAKE) clean) > /dev/null || true
        -(cd jemalloc && [ -f Makefile ] && $(MAKE) distclean) > /dev/null || true
-       -(rm -f .make-arch)
+       -(rm -f .make-*)
+
+.PHONY: distclean
+
+hiredis: .make-prerequisites
+       @printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
+       cd hiredis && $(MAKE) static
+
+.PHONY: hiredis
+
+linenoise: .make-prerequisites
+       @printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
+       cd linenoise && $(MAKE)
+
+.PHONY: linenoise
+
+ifeq ($(uname_S),SunOS)
+  # Make isinf() available
+  LUA_CFLAGS= -D__C99FEATURES__=1
+endif
+
+LUA_CFLAGS+= -O2 -Wall -DLUA_ANSI $(CFLAGS)
+LUA_LDFLAGS+= $(LDFLAGS)
 
-hiredis: .make-arch
-       @printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)hiredis$(ENDCOLOR)
-       cd hiredis && $(MAKE) static ARCH="$(ARCH)"
+lua: .make-prerequisites
+       @printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
+       cd lua/src && $(MAKE) all CFLAGS="$(LUA_CFLAGS)" MYLDFLAGS="$(LUA_LDFLAGS)"
 
-linenoise: .make-arch
-       @printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)linenoise$(ENDCOLOR)
-       cd linenoise && $(MAKE) ARCH="$(ARCH)"
+.PHONY: lua
 
-lua: .make-arch
-       @printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)lua$(ENDCOLOR)
-       cd lua && $(MAKE) CFLAGS="$(LUA_CFLAGS)" MYLDFLAGS="$(ARCH)" ansi
+JEMALLOC_CFLAGS= -std=gnu99 -Wall -pipe -g3 -O3 -funroll-loops $(CFLAGS)
+JEMALLOC_LDFLAGS= $(LDFLAGS)
 
-jemalloc: .make-arch
-       @printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)jemalloc$(ENDCOLOR)
-       cd jemalloc && ./configure $(JEMALLOC_CFLAGS) --with-jemalloc-prefix=je_ --enable-cc-silence && $(MAKE) lib/libjemalloc.a
+jemalloc: .make-prerequisites
+       @printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
+       cd jemalloc && ./configure --with-jemalloc-prefix=je_ --enable-cc-silence CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)"
+       cd jemalloc && $(MAKE) lib/libjemalloc.a
 
-.PHONY: default conditional_clean hiredis linenoise lua jemalloc
+.PHONY: jemalloc
index 841f39072fff76ee6060581f0f264ecf8d5a6910..1dd894b49e9b992c8d9b22de75b30d21fcb63e81 100644 (file)
@@ -1,10 +1,21 @@
-linenoise_example: linenoise.h linenoise.c
+STD=
+WARN= -Wall
+OPT= -Os
+
+R_CFLAGS= $(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS)
+R_LDFLAGS= $(LDFLAGS)
+DEBUG= -g
+
+R_CC=$(CC) $(R_CFLAGS)
+R_LD=$(CC) $(R_LDFLAGS)
+
+linenoise.o: linenoise.h linenoise.c
 
 linenoise_example: linenoise.o example.o
-       $(CC) $(ARCH) -Wall -W -Os -g -o linenoise_example linenoise.o example.o
+       $(R_LD) -o $@ $^
 
 .c.o:
-       $(CC) $(ARCH) -c -Wall -W -Os -g $<
+       $(R_CC) -c $<
 
 clean:
        rm -f linenoise_example *.o
index 1b79e09efe1fde338145306304c96b922f0c4526..b1ea6c2ee4d2419860f0c0a0c5cf15c8850e9a96 100644 (file)
@@ -114,6 +114,15 @@ stop-writes-on-bgsave-error yes
 # the dataset will likely be bigger if you have compressible values or keys.
 rdbcompression yes
 
+# Since verison 5 of RDB a CRC64 checksum is placed at the end of the file.
+# This makes the format more resistant to corruption but there is a performance
+# hit to pay (around 10%) when saving and loading RDB files, so you can disable it
+# for maximum performances.
+#
+# RDB files created with checksum disabled have a checksum of zero that will
+# tell the loading code to skip the check.
+rdbchecksum yes
+
 # The filename where to dump the DB
 dbfilename dump.rdb
 
@@ -289,21 +298,23 @@ slave-read-only yes
 
 ############################## APPEND ONLY MODE ###############################
 
-# By default Redis asynchronously dumps the dataset on disk. If you can live
-# with the idea that the latest records will be lost if something like a crash
-# happens this is the preferred way to run Redis. If instead you care a lot
-# about your data and don't want to that a single record can get lost you should
-# enable the append only mode: when this mode is enabled Redis will append
-# every write operation received in the file appendonly.aof. This file will
-# be read on startup in order to rebuild the full dataset in memory.
+# By default Redis asynchronously dumps the dataset on disk. This mode is
+# good enough in many applications, but an issue with the Redis process or
+# a power outage may result into a few minutes of writes lost (depending on
+# the configured save points).
 #
-# Note that you can have both the async dumps and the append only file if you
-# like (you have to comment the "save" statements above to disable the dumps).
-# Still if append only mode is enabled Redis will load the data from the
-# log file at startup ignoring the dump.rdb file.
+# The Append Only File is an alternative persistence mode that provides
+# much better durability. For instance using the default data fsync policy
+# (see later in the config file) Redis can lose just one second of writes in a
+# dramatic event like a server power outage, or a single write if something
+# wrong with the Redis process itself happens, but the operating system is
+# still running correctly.
 #
-# IMPORTANT: Check the BGREWRITEAOF to check how to rewrite the append
-# log file in background when it gets too big.
+# AOF and RDB persistence can be enabled at the same time without problems.
+# If the AOF is enabled on startup Redis will load the AOF, that is the file
+# with the better durability guarantees.
+#
+# Please check http://redis.io/topics/persistence for more information.
 
 appendonly no
 
@@ -318,7 +329,7 @@ appendonly no
 #
 # no: don't fsync, just let the OS flush the data when it wants. Faster.
 # always: fsync after every write to the append only log . Slow, Safest.
-# everysec: fsync only if one second passed since the last fsync. Compromise.
+# everysec: fsync only one time every second. Compromise.
 #
 # The default is "everysec" that's usually the right compromise between
 # speed and data safety. It's up to you to understand if you can relax this to
@@ -328,6 +339,9 @@ appendonly no
 # or on the contrary, use "always" that's very slow but a bit safer than
 # everysec.
 #
+# More details please check the following article:
+# http://antirez.com/post/redis-persistence-demystified.html
+#
 # If unsure, use "everysec".
 
 # appendfsync always
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644 (file)
index 0000000..aee7aac
--- /dev/null
@@ -0,0 +1,5 @@
+*.gcda
+*.gcno
+*.gcov
+redis.info
+lcov-html
index 43922c2de59720d0aaa6a374926611c08c78bad6..a0913688429539a7fbacabcd0df52fa5ff7e4e92 100644 (file)
@@ -1,27 +1,32 @@
 # Redis Makefile
 # Copyright (C) 2009 Salvatore Sanfilippo <antirez at gmail dot com>
 # This file is released under the BSD license, see the COPYING file
+#
+# The Makefile composes the final FINAL_CFLAGS and FINAL_LDFLAGS using
+# what is needed for Redis plus the standard CFLAGS and LDFLAGS passed.
+# However when building the dependencies (Jemalloc, Lua, Hiredis, ...)
+# CFLAGS and LDFLAGS are propagated to the dependencies, so to pass
+# flags only to be used when compiling / linking Redis itself REDIS_CFLAGS
+# and REDIS_LDFLAGS are used instead (this is the case of 'make gcov').
+#
+# Dependencies are stored in the Makefile.dep file. To rebuild this file
+# Just use 'make dep', but this is only needed by developers.
 
 release_hdr := $(shell sh -c './mkreleasehdr.sh')
 uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
 OPTIMIZATION?=-O2
 DEPENDENCY_TARGETS=hiredis linenoise lua
 
-ifeq ($(uname_S),SunOS)
-  CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -Wall -W -D__EXTENSIONS__ -D_XPG6
-  CCLINK?=-ldl -lnsl -lsocket -lm -lpthread
-  DEBUG?=-g -ggdb 
-else
-  CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -Wall -W $(ARCH) $(PROF)
-  CCLINK?=-lm -pthread
-  DEBUG?=-g -rdynamic -ggdb 
-endif
+# Default settings
+STD= -std=c99 -pedantic
+WARN= -Wall
+OPT= $(OPTIMIZATION)
 
 # Default allocator
 ifeq ($(uname_S),Linux)
-  MALLOC?=jemalloc
+  MALLOC=jemalloc
 else
-  MALLOC?=libc
+  MALLOC=libc
 endif
 
 # Backwards compatibility for selecting an allocator
@@ -37,25 +42,42 @@ ifeq ($(USE_JEMALLOC),yes)
   MALLOC=jemalloc
 endif
 
+# Override default settings if possible
+-include .make-settings
+
+ifeq ($(uname_S),SunOS)
+  FINAL_CFLAGS= $(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS) -D__EXTENSIONS__ -D_XPG6
+  FINAL_LDFLAGS= $(LDFLAGS) $(REDIS_LDFLAGS)
+  FINAL_LIBS= -ldl -lnsl -lsocket -lm -lpthread
+  DEBUG= -g -ggdb
+else
+  FINAL_CFLAGS= $(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS)
+  FINAL_LDFLAGS= $(LDFLAGS) $(REDIS_LDFLAGS)
+  FINAL_LIBS= -lm -pthread
+  DEBUG= -g -rdynamic -ggdb
+endif
+
+# Include paths to dependencies
+FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src
+
 ifeq ($(MALLOC),tcmalloc)
-  ALLOC_LINK=-ltcmalloc
-  ALLOC_FLAGS=-DUSE_TCMALLOC
+  FINAL_CFLAGS+= -DUSE_TCMALLOC
+  FINAL_LIBS+= -ltcmalloc
 endif
 
 ifeq ($(MALLOC),tcmalloc_minimal)
-  ALLOC_LINK=-ltcmalloc_minimal
-  ALLOC_FLAGS=-DUSE_TCMALLOC
+  FINAL_CFLAGS+= -DUSE_TCMALLOC
+  FINAL_LIBS+= -ltcmalloc_minimal
 endif
 
 ifeq ($(MALLOC),jemalloc)
-  ALLOC_LINK=../deps/jemalloc/lib/libjemalloc.a -ldl
-  ALLOC_FLAGS=-DUSE_JEMALLOC -I../deps/jemalloc/include
   DEPENDENCY_TARGETS+= jemalloc
+  FINAL_CFLAGS+= -DUSE_JEMALLOC -I../deps/jemalloc/include
+  FINAL_LIBS+= ../deps/jemalloc/lib/libjemalloc.a -ldl
 endif
 
-CCLINK+= $(ALLOC_LINK)
-CFLAGS+= $(ALLOC_FLAGS)
-CCOPT= $(CFLAGS) $(ARCH) $(PROF)
+REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS)
+REDIS_LD=$(QUIET_LINK)$(CC) $(FINAL_LDFLAGS)
 
 PREFIX= /usr/local
 INSTALL_BIN= $(PREFIX)/bin
@@ -69,209 +91,134 @@ 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);
+QUIET_CC = @printf '    %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR) 1>&2;
+QUIET_LINK = @printf '    %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2;
 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 endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.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
-CHECKAOFOBJ = redis-check-aof.o
-
-PRGNAME = redis-server
-BENCHPRGNAME = redis-benchmark
-CLIPRGNAME = redis-cli
-CHECKDUMPPRGNAME = redis-check-dump
-CHECKAOFPRGNAME = redis-check-aof
-
-all: redis-benchmark redis-cli redis-check-dump redis-check-aof redis-server
+REDIS_SERVER_NAME= redis-server
+REDIS_SERVER_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 endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o
+REDIS_CLI_NAME= redis-cli
+REDIS_CLI_OBJ= anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o
+REDIS_BENCHMARK_NAME= redis-benchmark
+REDIS_BENCHMARK_OBJ= ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o redis-benchmark.o
+REDIS_CHECK_DUMP_NAME= redis-check-dump
+REDIS_CHECK_DUMP_OBJ= redis-check-dump.o lzf_c.o lzf_d.o
+REDIS_CHECK_AOF_NAME= redis-check-aof
+REDIS_CHECK_AOF_OBJ= redis-check-aof.o
+
+all: $(REDIS_SERVER_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_DUMP_NAME) $(REDIS_CHECK_AOF_NAME)
        @echo ""
        @echo "Hint: To run 'make test' is a good idea ;)"
        @echo ""
 
-# Deps (use make dep to generate this)
-adlist.o: adlist.c adlist.h zmalloc.h
-ae.o: ae.c ae.h zmalloc.h config.h ae_kqueue.c
-ae_epoll.o: ae_epoll.c
-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 ziplist.h intset.h version.h util.h rdb.h rio.h bio.h
-bio.o: bio.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
-  zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h bio.h
-cluster.o: cluster.c redis.h fmacros.h config.h ae.h sds.h dict.h \
-  adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \
-  rio.h
-config.o: config.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
-  zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h
-crc16.o: crc16.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
-  zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h
-db.o: db.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
-  zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h
-debug.o: debug.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
-  zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h sha1.h
-dict.o: dict.c fmacros.h dict.h zmalloc.h
-endianconv.o: endianconv.c
-intset.o: intset.c intset.h zmalloc.h endianconv.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 ziplist.h intset.h version.h util.h rdb.h rio.h
-networking.o: networking.c redis.h fmacros.h config.h ae.h sds.h dict.h \
-  adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \
-  rio.h
-object.o: object.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
-  zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.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 ziplist.h intset.h version.h util.h rdb.h rio.h
-rand.o: rand.c
-rdb.o: rdb.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
-  zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h rio.h lzf.h \
-  zipmap.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 ../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 ziplist.h intset.h version.h util.h rdb.h rio.h \
-  slowlog.h bio.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 ziplist.h intset.h version.h util.h rdb.h \
-  rio.h
-rio.o: rio.c fmacros.h rio.h sds.h util.h
-scripting.o: scripting.c redis.h fmacros.h config.h ae.h sds.h dict.h \
-  adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \
-  rio.h sha1.h rand.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 ziplist.h intset.h version.h util.h rdb.h \
-  rio.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 ziplist.h intset.h version.h util.h rdb.h rio.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 ziplist.h intset.h version.h util.h rdb.h rio.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 ziplist.h intset.h version.h util.h rdb.h rio.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 ziplist.h intset.h version.h util.h rdb.h rio.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 ziplist.h intset.h version.h util.h rdb.h rio.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 ziplist.h intset.h version.h util.h rdb.h \
-  rio.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 ziplist.h intset.h version.h util.h rdb.h rio.h
-util.o: util.c fmacros.h util.h
-ziplist.o: ziplist.c zmalloc.h util.h ziplist.h endianconv.h
-zipmap.o: zipmap.c zmalloc.h endianconv.h
-zmalloc.o: zmalloc.c config.h zmalloc.h
-
-# Clean local objects when ARCH is different
-ifneq ($(shell sh -c '[ -f .make-arch ] && cat .make-arch'), $(ARCH))
-.make-arch: clean
-else
-.make-arch:
-endif
+.PHONY: all
 
-.make-arch:
-       -(cd ../deps && $(MAKE) $(DEPENDENCY_TARGETS) ARCH="$(ARCH)")
-       -(echo $(ARCH) > .make-arch)
-
-# Clean local objects when allocator changes
-ifneq ($(shell sh -c '[ -f .make-malloc ] && cat .make-malloc'), $(MALLOC))
-.make-malloc: clean
-else
-.make-malloc:
-endif
-
-.make-malloc:
-       -(echo $(MALLOC) > .make-malloc)
+# Deps (use make dep to generate this)
+include Makefile.dep
 
-# Union of make-prerequisites
-.make-prerequisites: .make-arch .make-malloc
+dep:
+       $(REDIS_CC) -MM *.c > Makefile.dep
+
+.PHONY: dep
+
+persist-settings: distclean
+       echo STD=$(STD) >> .make-settings
+       echo WARN=$(WARN) >> .make-settings
+       echo OPT=$(OPT) >> .make-settings
+       echo MALLOC=$(MALLOC) >> .make-settings
+       echo CFLAGS=$(CFLAGS) >> .make-settings
+       echo LDFLAGS=$(LDFLAGS) >> .make-settings
+       echo REDIS_CFLAGS=$(REDIS_CFLAGS) >> .make-settings
+       echo REDIS_LDFLAGS=$(REDIS_LDFLAGS) >> .make-settings
+       echo PREV_FINAL_CFLAGS=$(FINAL_CFLAGS) >> .make-settings
+       echo PREV_FINAL_LDFLAGS=$(FINAL_LDFLAGS) >> .make-settings
+       -(cd ../deps && $(MAKE) $(DEPENDENCY_TARGETS))
+
+.PHONY: persist-settings
+
+# Prerequisites target
+.make-prerequisites:
        @touch $@
 
-redis-server: .make-prerequisites $(OBJ)
-       $(QUIET_LINK)$(CC) -o $(PRGNAME) $(CCOPT) $(DEBUG) $(OBJ) ../deps/lua/src/liblua.a $(CCLINK)
+# Clean everything, persist settings and build dependencies if anything changed
+ifneq ($(strip $(PREV_FINAL_CFLAGS)), $(strip $(FINAL_CFLAGS)))
+.make-prerequisites: persist-settings
+endif
 
-redis-benchmark: .make-prerequisites $(BENCHOBJ)
-       $(QUIET_LINK)$(CC) -o $(BENCHPRGNAME) $(CCOPT) $(DEBUG) $(BENCHOBJ) ../deps/hiredis/libhiredis.a $(CCLINK)
+ifneq ($(strip $(PREV_FINAL_LDFLAGS)), $(strip $(FINAL_LDFLAGS)))
+.make-prerequisites: persist-settings
+endif
 
-redis-benchmark.o: redis-benchmark.c .make-prerequisites
-       $(QUIET_CC)$(CC) -c $(CFLAGS) -I../deps/hiredis $(DEBUG) $(COMPILE_TIME) $<
+# redis-server
+$(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ)
+       $(REDIS_LD) -o $@ $^ ../deps/lua/src/liblua.a $(FINAL_LIBS)
 
-redis-cli: .make-prerequisites $(CLIOBJ)
-       $(QUIET_LINK)$(CC) -o $(CLIPRGNAME) $(CCOPT) $(DEBUG) $(CLIOBJ) ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(CCLINK)
+# redis-cli
+$(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)
+       $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS)
 
-redis-cli.o: redis-cli.c .make-prerequisites
-       $(QUIET_CC)$(CC) -c $(CFLAGS) -I../deps/hiredis -I../deps/linenoise $(DEBUG) $(COMPILE_TIME) $<
+# redis-benchmark
+$(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ)
+       $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a $(FINAL_LIBS)
 
-redis-check-dump: .make-prerequisites $(CHECKDUMPOBJ)
-       $(QUIET_LINK)$(CC) -o $(CHECKDUMPPRGNAME) $(CCOPT) $(DEBUG) $(CHECKDUMPOBJ) $(CCLINK)
+# redis-check-dump
+$(REDIS_CHECK_DUMP_NAME): $(REDIS_CHECK_DUMP_OBJ)
+       $(REDIS_LD) -o $@ $^ $(FINAL_LIBS)
 
-redis-check-aof: .make-prerequisites $(CHECKAOFOBJ)
-       $(QUIET_LINK)$(CC) -o $(CHECKAOFPRGNAME) $(CCOPT) $(DEBUG) $(CHECKAOFOBJ) $(CCLINK)
+# redis-check-aof
+$(REDIS_CHECK_AOF_NAME): $(REDIS_CHECK_AOF_OBJ)
+       $(REDIS_LD) -o $@ $^ $(FINAL_LIBS)
 
-# Because the jemalloc.h header is generated as a part of the jemalloc build
-# process, building it should complete before building any other object. Instead of
-# depending on a single artifact, simply build all dependencies first.
+# Because the jemalloc.h header is generated as a part of the jemalloc build,
+# building it should complete before building any other object. Instead of
+# depending on a single artifact, build all dependencies first.
 %.o: %.c .make-prerequisites
-       $(QUIET_CC)$(CC) -c $(CFLAGS) $(DEBUG) $(COMPILE_TIME) -I../deps/lua/src $<
-
-.PHONY: all clean distclean
+       $(REDIS_CC) -c $<
 
 clean:
-       rm -rf $(PRGNAME) $(BENCHPRGNAME) $(CLIPRGNAME) $(CHECKDUMPPRGNAME) $(CHECKAOFPRGNAME) *.o *.gcda *.gcno *.gcov
+       rm -rf $(REDIS_SERVER_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_DUMP_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html
+
+.PHONY: clean
 
 distclean: clean
        -(cd ../deps && $(MAKE) distclean)
-       -(rm -f .make-arch .make-malloc)
+       -(rm -f .make-*)
 
-dep:
-       $(CC) -MM *.c -I ../deps/hiredis -I ../deps/linenoise
+.PHONY: distclean
 
-test: redis-server redis-check-aof
+test: $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME)
        @(cd ..; ./runtest)
 
-bench:
-       ./redis-benchmark
+lcov:
+       $(MAKE) gcov
+       @(set -e; cd ..; ./runtest --clients 1)
+       @geninfo -o redis.info .
+       @genhtml --legend -o lcov-html redis.info
 
-log:
-       git log '--pretty=format:%ad %s (%cn)' --date=short > ../Changelog
+.PHONY: lcov
+
+bench: $(REDIS_BENCHMARK_NAME)
+       ./$(REDIS_BENCHMARK_NAME)
 
 32bit:
        @echo ""
        @echo "WARNING: if it fails under Linux you probably need to install libc6-dev-i386"
        @echo ""
-       $(MAKE) ARCH="-m32" JEMALLOC_CFLAGS='CFLAGS="-std=gnu99 -Wall -pipe -g3 -fvisibility=hidden -O3 -funroll-loops -m32"'
-
-gprof:
-       $(MAKE) PROF="-pg"
+       $(MAKE) CFLAGS="-m32" LDFLAGS="-m32"
 
 gcov:
-       $(MAKE) PROF="-fprofile-arcs -ftest-coverage"
+       $(MAKE) REDIS_CFLAGS="-fprofile-arcs -ftest-coverage -DCOVERAGE_TEST" REDIS_LDFLAGS="-fprofile-arcs -ftest-coverage"
 
 noopt:
-       $(MAKE) OPTIMIZATION=""
-
-32bitgprof:
-       $(MAKE) PROF="-pg" ARCH="-arch i386"
+       $(MAKE) OPT="-O0"
 
 src/help.h:
        @../utils/generate-command-help.rb > help.h
 
 install: all
        mkdir -p $(INSTALL_BIN)
-       $(INSTALL) $(PRGNAME) $(INSTALL_BIN)
-       $(INSTALL) $(BENCHPRGNAME) $(INSTALL_BIN)
-       $(INSTALL) $(CLIPRGNAME) $(INSTALL_BIN)
-       $(INSTALL) $(CHECKDUMPPRGNAME) $(INSTALL_BIN)
-       $(INSTALL) $(CHECKAOFPRGNAME) $(INSTALL_BIN)
+       $(INSTALL) $(REDIS_SERVER_NAME) $(INSTALL_BIN)
+       $(INSTALL) $(REDIS_BENCHMARK_NAME) $(INSTALL_BIN)
+       $(INSTALL) $(REDIS_CLI_NAME) $(INSTALL_BIN)
+       $(INSTALL) $(REDIS_CHECK_DUMP_NAME) $(INSTALL_BIN)
+       $(INSTALL) $(REDIS_CHECK_AOF_NAME) $(INSTALL_BIN)
diff --git a/src/Makefile.dep b/src/Makefile.dep
new file mode 100644 (file)
index 0000000..d0254e8
--- /dev/null
@@ -0,0 +1,104 @@
+adlist.o: adlist.c adlist.h zmalloc.h
+ae.o: ae.c ae.h zmalloc.h config.h ae_kqueue.c
+ae_epoll.o: ae_epoll.c
+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 ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h bio.h
+bio.o: bio.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h bio.h
+cluster.o: cluster.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h endianconv.h
+config.o: config.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h
+crc16.o: crc16.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h
+crc64.o: crc64.c
+db.o: db.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h
+debug.o: debug.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h sha1.h
+dict.o: dict.c fmacros.h dict.h zmalloc.h
+endianconv.o: endianconv.c
+intset.o: intset.c intset.h zmalloc.h endianconv.h
+lzf_c.o: lzf_c.c lzfP.h
+lzf_d.o: lzf_d.c lzfP.h
+memtest.o: memtest.c
+multi.o: multi.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h
+networking.o: networking.c redis.h fmacros.h config.h \
+  ../deps/lua/src/lua.h ../deps/lua/src/luaconf.h ae.h sds.h dict.h \
+  adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \
+  rio.h
+object.o: object.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h
+pqsort.o: pqsort.c
+pubsub.o: pubsub.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h
+rand.o: rand.c
+rdb.o: rdb.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h lzf.h zipmap.h \
+  endianconv.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 ../deps/hiredis/hiredis.h \
+  sds.h zmalloc.h ../deps/linenoise/linenoise.h help.h
+redis.o: redis.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h slowlog.h bio.h \
+  asciilogo.h
+release.o: release.c release.h
+replication.o: replication.c redis.h fmacros.h config.h \
+  ../deps/lua/src/lua.h ../deps/lua/src/luaconf.h ae.h sds.h dict.h \
+  adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \
+  rio.h
+rio.o: rio.c fmacros.h rio.h sds.h util.h
+scripting.o: scripting.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h sha1.h rand.h \
+  ../deps/lua/src/lauxlib.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/lualib.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 ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h slowlog.h
+sort.o: sort.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h pqsort.h
+syncio.o: syncio.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h
+t_hash.o: t_hash.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h
+t_list.o: t_list.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h
+t_set.o: t_set.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h
+t_string.o: t_string.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h
+t_zset.o: t_zset.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
+  ../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
+  ziplist.h intset.h version.h util.h rdb.h rio.h
+util.o: util.c fmacros.h util.h
+ziplist.o: ziplist.c zmalloc.h util.h ziplist.h endianconv.h
+zipmap.o: zipmap.c zmalloc.h endianconv.h
+zmalloc.o: zmalloc.c config.h zmalloc.h
index 4099b12598f4ea2a740590a86d44f30b183bb948..668277a78087c102627b15107c35f7876d0035c0 100644 (file)
--- a/src/ae.c
+++ b/src/ae.c
@@ -35,6 +35,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <stdlib.h>
+#include <poll.h>
 #include <string.h>
 
 #include "ae.h"
@@ -369,21 +370,17 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
 /* Wait for millseconds until the given file descriptor becomes
  * writable/readable/exception */
 int aeWait(int fd, int mask, long long milliseconds) {
-    struct timeval tv;
-    fd_set rfds, wfds, efds;
+    struct pollfd pfd;
     int retmask = 0, retval;
 
-    tv.tv_sec = milliseconds/1000;
-    tv.tv_usec = (milliseconds%1000)*1000;
-    FD_ZERO(&rfds);
-    FD_ZERO(&wfds);
-    FD_ZERO(&efds);
-
-    if (mask & AE_READABLE) FD_SET(fd,&rfds);
-    if (mask & AE_WRITABLE) FD_SET(fd,&wfds);
-    if ((retval = select(fd+1, &rfds, &wfds, &efds, &tv)) > 0) {
-        if (FD_ISSET(fd,&rfds)) retmask |= AE_READABLE;
-        if (FD_ISSET(fd,&wfds)) retmask |= AE_WRITABLE;
+    memset(&pfd, 0, sizeof(pfd));
+    pfd.fd = fd;
+    if (mask & AE_READABLE) pfd.events |= POLLIN;
+    if (mask & AE_WRITABLE) pfd.events |= POLLOUT;
+
+    if ((retval = poll(&pfd, 1, milliseconds))== 1) {
+        if (pfd.revents & POLLIN) retmask |= AE_READABLE;
+        if (pfd.revents & POLLOUT) retmask |= AE_WRITABLE;
         return retmask;
     } else {
         return retval;
index ba4e6cce89bbc7a552e8510a03a2cb67c53af1f6..434d945c7d850edd8b8a8934d254bbc88e5d62d0 100644 (file)
@@ -262,7 +262,11 @@ static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len) {
         close(s);
         return ANET_ERR;
     }
-    if (listen(s, 511) == -1) { /* the magic 511 constant is from nginx */
+
+    /* Use a backlog of 512 entries. We pass 511 to the listen() call because
+     * the kernel does: backlogsize = roundup_pow_of_two(backlogsize + 1);
+     * which will thus give us a backlog of 512 entries */
+    if (listen(s, 511) == -1) {
         anetSetError(err, "listen: %s", strerror(errno));
         close(s);
         return ANET_ERR;
index 3f7cd10ffb87b1da7e4d0039b55c5d3a10c1dca3..115da29bae2fd7243b823ab6c3b0475dbadcdcdc 100644 (file)
--- a/src/aof.c
+++ b/src/aof.c
@@ -803,9 +803,9 @@ int rewriteAppendOnlyFileBackground(void) {
         if (server.sofd > 0) close(server.sofd);
         snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid());
         if (rewriteAppendOnlyFile(tmpfile) == REDIS_OK) {
-            _exit(0);
+            exitFromChild(0);
         } else {
-            _exit(1);
+            exitFromChild(1);
         }
     } else {
         /* Parent */
index f76e8ff5cf8b1e57d9a1e363ecb8116ac1180bdb..93f095c34828fe84a66dc3f4e8fefcf27630ff44 100644 (file)
@@ -1,4 +1,5 @@
 #include "redis.h"
+#include "endianconv.h"
 
 #include <arpa/inet.h>
 #include <fcntl.h>
@@ -1457,9 +1458,86 @@ void clusterCommand(redisClient *c) {
 }
 
 /* -----------------------------------------------------------------------------
- * RESTORE and MIGRATE commands
+ * DUMP, RESTORE and MIGRATE commands
  * -------------------------------------------------------------------------- */
 
+/* Generates a DUMP-format representation of the object 'o', adding it to the
+ * io stream pointed by 'rio'. This function can't fail. */
+void createDumpPayload(rio *payload, robj *o) {
+    unsigned char buf[2];
+    uint64_t crc;
+
+    /* Serialize the object in a RDB-like format. It consist of an object type
+     * byte followed by the serialized object. This is understood by RESTORE. */
+    rioInitWithBuffer(payload,sdsempty());
+    redisAssert(rdbSaveObjectType(payload,o));
+    redisAssert(rdbSaveObject(payload,o));
+
+    /* Write the footer, this is how it looks like:
+     * ----------------+---------------------+---------------+
+     * ... RDB payload | 2 bytes RDB version | 8 bytes CRC64 |
+     * ----------------+---------------------+---------------+
+     * RDB version and CRC are both in little endian.
+     */
+
+    /* RDB version */
+    buf[0] = REDIS_RDB_VERSION & 0xff;
+    buf[1] = (REDIS_RDB_VERSION >> 8) & 0xff;
+    payload->io.buffer.ptr = sdscatlen(payload->io.buffer.ptr,buf,2);
+
+    /* CRC64 */
+    crc = crc64(0,(unsigned char*)payload->io.buffer.ptr,
+                sdslen(payload->io.buffer.ptr));
+    memrev64ifbe(&crc);
+    payload->io.buffer.ptr = sdscatlen(payload->io.buffer.ptr,&crc,8);
+}
+
+/* Verify that the RDB version of the dump payload matches the one of this Redis
+ * instance and that the checksum is ok.
+ * If the DUMP payload looks valid REDIS_OK is returned, otherwise REDIS_ERR
+ * is returned. */
+int verifyDumpPayload(unsigned char *p, size_t len) {
+    unsigned char *footer;
+    uint16_t rdbver;
+    uint64_t crc;
+
+    /* At least 2 bytes of RDB version and 8 of CRC64 should be present. */
+    if (len < 10) return REDIS_ERR;
+    footer = p+(len-10);
+
+    /* Verify RDB version */
+    rdbver = (footer[1] << 8) | footer[0];
+    if (rdbver != REDIS_RDB_VERSION) return REDIS_ERR;
+
+    /* Verify CRC64 */
+    crc = crc64(0,p,len-8);
+    memrev64ifbe(&crc);
+    return (memcmp(&crc,footer+2,8) == 0) ? REDIS_OK : REDIS_ERR;
+}
+
+/* DUMP keyname
+ * DUMP is actually not used by Redis Cluster but it is the obvious
+ * complement of RESTORE and can be useful for different applications. */
+void dumpCommand(redisClient *c) {
+    robj *o, *dumpobj;
+    rio payload;
+
+    /* Check if the key is here. */
+    if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) {
+        addReply(c,shared.nullbulk);
+        return;
+    }
+
+    /* Create the DUMP encoded representation. */
+    createDumpPayload(&payload,o);
+
+    /* Transfer to the client */
+    dumpobj = createObject(REDIS_STRING,payload.io.buffer.ptr);
+    addReplyBulk(c,dumpobj);
+    decrRefCount(dumpobj);
+    return;
+}
+
 /* RESTORE key ttl serialized-value */
 void restoreCommand(redisClient *c) {
     long ttl;
@@ -1481,6 +1559,12 @@ void restoreCommand(redisClient *c) {
         return;
     }
 
+    /* Verify RDB version and data checksum. */
+    if (verifyDumpPayload(c->argv[3]->ptr,sdslen(c->argv[3]->ptr)) == REDIS_ERR) {
+        addReplyError(c,"DUMP payload version or checksum are wrong");
+        return;
+    }
+
     rioInitWithBuffer(&payload,c->argv[3]->ptr);
     if (((type = rdbLoadObjectType(&payload)) == -1) ||
         ((obj = rdbLoadObject(type,&payload)) == NULL))
@@ -1491,7 +1575,7 @@ void restoreCommand(redisClient *c) {
 
     /* Create the key and set the TTL if any */
     dbAdd(c->db,c->argv[1],obj);
-    if (ttl) setExpire(c->db,c->argv[1],time(NULL)+ttl);
+    if (ttl) setExpire(c->db,c->argv[1],mstime()+ttl);
     signalModifiedKey(c->db,c->argv[1]);
     addReply(c,shared.ok);
     server.dirty++;
@@ -1502,7 +1586,7 @@ void migrateCommand(redisClient *c) {
     int fd;
     long timeout;
     long dbid;
-    time_t ttl;
+    long long ttl = 0, expireat;
     robj *o;
     rio cmd, payload;
 
@@ -1530,28 +1614,32 @@ void migrateCommand(redisClient *c) {
         return;
     }
     if ((aeWait(fd,AE_WRITABLE,timeout*1000) & AE_WRITABLE) == 0) {
-        addReplyError(c,"Timeout connecting to the client");
+        addReplySds(c,sdsnew("-IOERR error or timeout connecting to the client\r\n"));
         return;
     }
 
+    /* Create RESTORE payload and generate the protocol to call the command. */
     rioInitWithBuffer(&cmd,sdsempty());
     redisAssertWithInfo(c,NULL,rioWriteBulkCount(&cmd,'*',2));
     redisAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,"SELECT",6));
     redisAssertWithInfo(c,NULL,rioWriteBulkLongLong(&cmd,dbid));
 
-    ttl = getExpire(c->db,c->argv[3]);
+    expireat = getExpire(c->db,c->argv[3]);
+    if (expireat != -1) {
+        ttl = expireat-mstime();
+        if (ttl < 1) ttl = 1;
+    }
     redisAssertWithInfo(c,NULL,rioWriteBulkCount(&cmd,'*',4));
     redisAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,"RESTORE",7));
     redisAssertWithInfo(c,NULL,c->argv[3]->encoding == REDIS_ENCODING_RAW);
     redisAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,c->argv[3]->ptr,sdslen(c->argv[3]->ptr)));
-    redisAssertWithInfo(c,NULL,rioWriteBulkLongLong(&cmd,(ttl == -1) ? 0 : ttl));
+    redisAssertWithInfo(c,NULL,rioWriteBulkLongLong(&cmd,ttl));
 
     /* Finally the last argument that is the serailized object payload
-     * in the form: <type><rdb-serialized-object>. */
-    rioInitWithBuffer(&payload,sdsempty());
-    redisAssertWithInfo(c,NULL,rdbSaveObjectType(&payload,o));
-    redisAssertWithInfo(c,NULL,rdbSaveObject(&payload,o) != -1);
-    redisAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,payload.io.buffer.ptr,sdslen(payload.io.buffer.ptr)));
+     * in the DUMP format. */
+    createDumpPayload(&payload,o);
+    redisAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,payload.io.buffer.ptr,
+                                sdslen(payload.io.buffer.ptr)));
     sdsfree(payload.io.buffer.ptr);
 
     /* Tranfer the query to the other node in 64K chunks. */
@@ -1562,7 +1650,7 @@ void migrateCommand(redisClient *c) {
 
         while ((towrite = sdslen(buf)-pos) > 0) {
             towrite = (towrite > (64*1024) ? (64*1024) : towrite);
-            nwritten = syncWrite(fd,buf+nwritten,towrite,timeout);
+            nwritten = syncWrite(fd,buf+pos,towrite,timeout);
             if (nwritten != (signed)towrite) goto socket_wr_err;
             pos += nwritten;
         }
@@ -1601,50 +1689,18 @@ void migrateCommand(redisClient *c) {
     return;
 
 socket_wr_err:
-    redisLog(REDIS_NOTICE,"Can't write to target node for MIGRATE: %s",
-        strerror(errno));
-    addReplyErrorFormat(c,"MIGRATE failed, writing to target node: %s.",
-        strerror(errno));
+    addReplySds(c,sdsnew("-IOERR error or timeout writing to target instance\r\n"));
     sdsfree(cmd.io.buffer.ptr);
     close(fd);
     return;
 
 socket_rd_err:
-    redisLog(REDIS_NOTICE,"Can't read from target node for MIGRATE: %s",
-        strerror(errno));
-    addReplyErrorFormat(c,"MIGRATE failed, reading from target node: %s.",
-        strerror(errno));
+    addReplySds(c,sdsnew("-IOERR error or timeout reading from target node\r\n"));
     sdsfree(cmd.io.buffer.ptr);
     close(fd);
     return;
 }
 
-/* DUMP keyname
- * DUMP is actually not used by Redis Cluster but it is the obvious
- * complement of RESTORE and can be useful for different applications. */
-void dumpCommand(redisClient *c) {
-    robj *o, *dumpobj;
-    rio payload;
-
-    /* Check if the key is here. */
-    if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) {
-        addReply(c,shared.nullbulk);
-        return;
-    }
-
-    /* Serialize the object in a RDB-like format. It consist of an object type
-     * byte followed by the serialized object. This is understood by RESTORE. */
-    rioInitWithBuffer(&payload,sdsempty());
-    redisAssertWithInfo(c,NULL,rdbSaveObjectType(&payload,o));
-    redisAssertWithInfo(c,NULL,rdbSaveObject(&payload,o));
-
-    /* Transfer to the client */
-    dumpobj = createObject(REDIS_STRING,payload.io.buffer.ptr);
-    addReplyBulk(c,dumpobj);
-    decrRefCount(dumpobj);
-    return;
-}
-
 /* The ASKING command is required after a -ASK redirection.
  * The client should issue ASKING before to actualy send the command to
  * the target instance. See the Redis Cluster specification for more
index 8dffe28873d509ab50fc6d5ca1f98301502e270a..6f98e5e5a8dd7a1d6e23a8198f447ea800518746 100644 (file)
@@ -155,6 +155,9 @@ void loadServerConfigFromString(char *config) {
             loadServerConfig(argv[1],NULL);
         } else if (!strcasecmp(argv[0],"maxclients") && argc == 2) {
             server.maxclients = atoi(argv[1]);
+            if (server.maxclients < 1) {
+                err = "Invalid max clients limit"; goto loaderr;
+            }
         } else if (!strcasecmp(argv[0],"maxmemory") && argc == 2) {
             server.maxmemory = memtoll(argv[1],NULL);
         } else if (!strcasecmp(argv[0],"maxmemory-policy") && argc == 2) {
@@ -210,6 +213,10 @@ void loadServerConfigFromString(char *config) {
             if ((server.rdb_compression = yesnotoi(argv[1])) == -1) {
                 err = "argument must be 'yes' or 'no'"; goto loaderr;
             }
+        } else if (!strcasecmp(argv[0],"rdbchecksum") && argc == 2) {
+            if ((server.rdb_checksum = yesnotoi(argv[1])) == -1) {
+                err = "argument must be 'yes' or 'no'"; goto loaderr;
+            }
         } else if (!strcasecmp(argv[0],"activerehashing") && argc == 2) {
             if ((server.activerehashing = yesnotoi(argv[1])) == -1) {
                 err = "argument must be 'yes' or 'no'"; goto loaderr;
@@ -633,6 +640,16 @@ void configSetCommand(redisClient *c) {
             enableWatchdog(ll);
         else
             disableWatchdog();
+    } else if (!strcasecmp(c->argv[2]->ptr,"rdbcompression")) {
+        int yn = yesnotoi(o->ptr);
+
+        if (yn == -1) goto badfmt;
+        server.rdb_compression = yn;
+    } else if (!strcasecmp(c->argv[2]->ptr,"rdbchecksum")) {
+        int yn = yesnotoi(o->ptr);
+
+        if (yn == -1) goto badfmt;
+        server.rdb_checksum = yn;
     } else {
         addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
             (char*)c->argv[2]->ptr);
@@ -734,6 +751,7 @@ void configGetCommand(redisClient *c) {
             server.stop_writes_on_bgsave_err);
     config_get_bool_field("daemonize", server.daemonize);
     config_get_bool_field("rdbcompression", server.rdb_compression);
+    config_get_bool_field("rdbchecksum", server.rdb_checksum);
     config_get_bool_field("activerehashing", server.activerehashing);
 
     /* Everything we can't handle with macros follows. */
index 6a69364a97158a2d7f46b82449046dadb200a9f2..136fd40c4c8eda0f89e975814de4127652c48ae8 100644 (file)
 #define        BIG_ENDIAN      4321    /* most-significant byte first (IBM, net) */
 #define        PDP_ENDIAN      3412    /* LSB first in word, MSW first in long (pdp)*/
 
-#if defined(vax) || defined(ns32000) || defined(sun386) || defined(__i386__) || \
-    defined(MIPSEL) || defined(_MIPSEL) || defined(BIT_ZERO_ON_RIGHT) || \
-    defined(__alpha__) || defined(__alpha)
-#define BYTE_ORDER     LITTLE_ENDIAN
+#if defined(__i386__) || defined(__x86_64__) || defined(__amd64__) || \
+   defined(vax) || defined(ns32000) || defined(sun386) || \
+   defined(MIPSEL) || defined(_MIPSEL) || defined(BIT_ZERO_ON_RIGHT) || \
+   defined(__alpha__) || defined(__alpha)
+#define BYTE_ORDER    LITTLE_ENDIAN
 #endif
 
 #if defined(sel) || defined(pyr) || defined(mc68000) || defined(sparc) || \
diff --git a/src/crc64.c b/src/crc64.c
new file mode 100644 (file)
index 0000000..ecdba90
--- /dev/null
@@ -0,0 +1,191 @@
+/* Redis uses the CRC64 variant with "Jones" coefficients and init value of 0.
+ *
+ * Specification of this CRC64 variant follows:
+ * Name: crc-64-jones
+ * Width: 64 bites
+ * Poly: 0xad93d23594c935a9
+ * Reflected In: True
+ * Xor_In: 0xffffffffffffffff
+ * Reflected_Out: True
+ * Xor_Out: 0x0
+ * Check("123456789"): 0xe9c6d914c4b8d9ca
+ *
+ * Copyright (c) 2012, Salvatore Sanfilippo <antirez at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE. */
+
+#include <stdint.h>
+
+static const uint64_t crc64_tab[256] = {
+    UINT64_C(0x0000000000000000), UINT64_C(0x7ad870c830358979),
+    UINT64_C(0xf5b0e190606b12f2), UINT64_C(0x8f689158505e9b8b),
+    UINT64_C(0xc038e5739841b68f), UINT64_C(0xbae095bba8743ff6),
+    UINT64_C(0x358804e3f82aa47d), UINT64_C(0x4f50742bc81f2d04),
+    UINT64_C(0xab28ecb46814fe75), UINT64_C(0xd1f09c7c5821770c),
+    UINT64_C(0x5e980d24087fec87), UINT64_C(0x24407dec384a65fe),
+    UINT64_C(0x6b1009c7f05548fa), UINT64_C(0x11c8790fc060c183),
+    UINT64_C(0x9ea0e857903e5a08), UINT64_C(0xe478989fa00bd371),
+    UINT64_C(0x7d08ff3b88be6f81), UINT64_C(0x07d08ff3b88be6f8),
+    UINT64_C(0x88b81eabe8d57d73), UINT64_C(0xf2606e63d8e0f40a),
+    UINT64_C(0xbd301a4810ffd90e), UINT64_C(0xc7e86a8020ca5077),
+    UINT64_C(0x4880fbd87094cbfc), UINT64_C(0x32588b1040a14285),
+    UINT64_C(0xd620138fe0aa91f4), UINT64_C(0xacf86347d09f188d),
+    UINT64_C(0x2390f21f80c18306), UINT64_C(0x594882d7b0f40a7f),
+    UINT64_C(0x1618f6fc78eb277b), UINT64_C(0x6cc0863448deae02),
+    UINT64_C(0xe3a8176c18803589), UINT64_C(0x997067a428b5bcf0),
+    UINT64_C(0xfa11fe77117cdf02), UINT64_C(0x80c98ebf2149567b),
+    UINT64_C(0x0fa11fe77117cdf0), UINT64_C(0x75796f2f41224489),
+    UINT64_C(0x3a291b04893d698d), UINT64_C(0x40f16bccb908e0f4),
+    UINT64_C(0xcf99fa94e9567b7f), UINT64_C(0xb5418a5cd963f206),
+    UINT64_C(0x513912c379682177), UINT64_C(0x2be1620b495da80e),
+    UINT64_C(0xa489f35319033385), UINT64_C(0xde51839b2936bafc),
+    UINT64_C(0x9101f7b0e12997f8), UINT64_C(0xebd98778d11c1e81),
+    UINT64_C(0x64b116208142850a), UINT64_C(0x1e6966e8b1770c73),
+    UINT64_C(0x8719014c99c2b083), UINT64_C(0xfdc17184a9f739fa),
+    UINT64_C(0x72a9e0dcf9a9a271), UINT64_C(0x08719014c99c2b08),
+    UINT64_C(0x4721e43f0183060c), UINT64_C(0x3df994f731b68f75),
+    UINT64_C(0xb29105af61e814fe), UINT64_C(0xc849756751dd9d87),
+    UINT64_C(0x2c31edf8f1d64ef6), UINT64_C(0x56e99d30c1e3c78f),
+    UINT64_C(0xd9810c6891bd5c04), UINT64_C(0xa3597ca0a188d57d),
+    UINT64_C(0xec09088b6997f879), UINT64_C(0x96d1784359a27100),
+    UINT64_C(0x19b9e91b09fcea8b), UINT64_C(0x636199d339c963f2),
+    UINT64_C(0xdf7adabd7a6e2d6f), UINT64_C(0xa5a2aa754a5ba416),
+    UINT64_C(0x2aca3b2d1a053f9d), UINT64_C(0x50124be52a30b6e4),
+    UINT64_C(0x1f423fcee22f9be0), UINT64_C(0x659a4f06d21a1299),
+    UINT64_C(0xeaf2de5e82448912), UINT64_C(0x902aae96b271006b),
+    UINT64_C(0x74523609127ad31a), UINT64_C(0x0e8a46c1224f5a63),
+    UINT64_C(0x81e2d7997211c1e8), UINT64_C(0xfb3aa75142244891),
+    UINT64_C(0xb46ad37a8a3b6595), UINT64_C(0xceb2a3b2ba0eecec),
+    UINT64_C(0x41da32eaea507767), UINT64_C(0x3b024222da65fe1e),
+    UINT64_C(0xa2722586f2d042ee), UINT64_C(0xd8aa554ec2e5cb97),
+    UINT64_C(0x57c2c41692bb501c), UINT64_C(0x2d1ab4dea28ed965),
+    UINT64_C(0x624ac0f56a91f461), UINT64_C(0x1892b03d5aa47d18),
+    UINT64_C(0x97fa21650afae693), UINT64_C(0xed2251ad3acf6fea),
+    UINT64_C(0x095ac9329ac4bc9b), UINT64_C(0x7382b9faaaf135e2),
+    UINT64_C(0xfcea28a2faafae69), UINT64_C(0x8632586aca9a2710),
+    UINT64_C(0xc9622c4102850a14), UINT64_C(0xb3ba5c8932b0836d),
+    UINT64_C(0x3cd2cdd162ee18e6), UINT64_C(0x460abd1952db919f),
+    UINT64_C(0x256b24ca6b12f26d), UINT64_C(0x5fb354025b277b14),
+    UINT64_C(0xd0dbc55a0b79e09f), UINT64_C(0xaa03b5923b4c69e6),
+    UINT64_C(0xe553c1b9f35344e2), UINT64_C(0x9f8bb171c366cd9b),
+    UINT64_C(0x10e3202993385610), UINT64_C(0x6a3b50e1a30ddf69),
+    UINT64_C(0x8e43c87e03060c18), UINT64_C(0xf49bb8b633338561),
+    UINT64_C(0x7bf329ee636d1eea), UINT64_C(0x012b592653589793),
+    UINT64_C(0x4e7b2d0d9b47ba97), UINT64_C(0x34a35dc5ab7233ee),
+    UINT64_C(0xbbcbcc9dfb2ca865), UINT64_C(0xc113bc55cb19211c),
+    UINT64_C(0x5863dbf1e3ac9dec), UINT64_C(0x22bbab39d3991495),
+    UINT64_C(0xadd33a6183c78f1e), UINT64_C(0xd70b4aa9b3f20667),
+    UINT64_C(0x985b3e827bed2b63), UINT64_C(0xe2834e4a4bd8a21a),
+    UINT64_C(0x6debdf121b863991), UINT64_C(0x1733afda2bb3b0e8),
+    UINT64_C(0xf34b37458bb86399), UINT64_C(0x8993478dbb8deae0),
+    UINT64_C(0x06fbd6d5ebd3716b), UINT64_C(0x7c23a61ddbe6f812),
+    UINT64_C(0x3373d23613f9d516), UINT64_C(0x49aba2fe23cc5c6f),
+    UINT64_C(0xc6c333a67392c7e4), UINT64_C(0xbc1b436e43a74e9d),
+    UINT64_C(0x95ac9329ac4bc9b5), UINT64_C(0xef74e3e19c7e40cc),
+    UINT64_C(0x601c72b9cc20db47), UINT64_C(0x1ac40271fc15523e),
+    UINT64_C(0x5594765a340a7f3a), UINT64_C(0x2f4c0692043ff643),
+    UINT64_C(0xa02497ca54616dc8), UINT64_C(0xdafce7026454e4b1),
+    UINT64_C(0x3e847f9dc45f37c0), UINT64_C(0x445c0f55f46abeb9),
+    UINT64_C(0xcb349e0da4342532), UINT64_C(0xb1eceec59401ac4b),
+    UINT64_C(0xfebc9aee5c1e814f), UINT64_C(0x8464ea266c2b0836),
+    UINT64_C(0x0b0c7b7e3c7593bd), UINT64_C(0x71d40bb60c401ac4),
+    UINT64_C(0xe8a46c1224f5a634), UINT64_C(0x927c1cda14c02f4d),
+    UINT64_C(0x1d148d82449eb4c6), UINT64_C(0x67ccfd4a74ab3dbf),
+    UINT64_C(0x289c8961bcb410bb), UINT64_C(0x5244f9a98c8199c2),
+    UINT64_C(0xdd2c68f1dcdf0249), UINT64_C(0xa7f41839ecea8b30),
+    UINT64_C(0x438c80a64ce15841), UINT64_C(0x3954f06e7cd4d138),
+    UINT64_C(0xb63c61362c8a4ab3), UINT64_C(0xcce411fe1cbfc3ca),
+    UINT64_C(0x83b465d5d4a0eece), UINT64_C(0xf96c151de49567b7),
+    UINT64_C(0x76048445b4cbfc3c), UINT64_C(0x0cdcf48d84fe7545),
+    UINT64_C(0x6fbd6d5ebd3716b7), UINT64_C(0x15651d968d029fce),
+    UINT64_C(0x9a0d8ccedd5c0445), UINT64_C(0xe0d5fc06ed698d3c),
+    UINT64_C(0xaf85882d2576a038), UINT64_C(0xd55df8e515432941),
+    UINT64_C(0x5a3569bd451db2ca), UINT64_C(0x20ed197575283bb3),
+    UINT64_C(0xc49581ead523e8c2), UINT64_C(0xbe4df122e51661bb),
+    UINT64_C(0x3125607ab548fa30), UINT64_C(0x4bfd10b2857d7349),
+    UINT64_C(0x04ad64994d625e4d), UINT64_C(0x7e7514517d57d734),
+    UINT64_C(0xf11d85092d094cbf), UINT64_C(0x8bc5f5c11d3cc5c6),
+    UINT64_C(0x12b5926535897936), UINT64_C(0x686de2ad05bcf04f),
+    UINT64_C(0xe70573f555e26bc4), UINT64_C(0x9ddd033d65d7e2bd),
+    UINT64_C(0xd28d7716adc8cfb9), UINT64_C(0xa85507de9dfd46c0),
+    UINT64_C(0x273d9686cda3dd4b), UINT64_C(0x5de5e64efd965432),
+    UINT64_C(0xb99d7ed15d9d8743), UINT64_C(0xc3450e196da80e3a),
+    UINT64_C(0x4c2d9f413df695b1), UINT64_C(0x36f5ef890dc31cc8),
+    UINT64_C(0x79a59ba2c5dc31cc), UINT64_C(0x037deb6af5e9b8b5),
+    UINT64_C(0x8c157a32a5b7233e), UINT64_C(0xf6cd0afa9582aa47),
+    UINT64_C(0x4ad64994d625e4da), UINT64_C(0x300e395ce6106da3),
+    UINT64_C(0xbf66a804b64ef628), UINT64_C(0xc5bed8cc867b7f51),
+    UINT64_C(0x8aeeace74e645255), UINT64_C(0xf036dc2f7e51db2c),
+    UINT64_C(0x7f5e4d772e0f40a7), UINT64_C(0x05863dbf1e3ac9de),
+    UINT64_C(0xe1fea520be311aaf), UINT64_C(0x9b26d5e88e0493d6),
+    UINT64_C(0x144e44b0de5a085d), UINT64_C(0x6e963478ee6f8124),
+    UINT64_C(0x21c640532670ac20), UINT64_C(0x5b1e309b16452559),
+    UINT64_C(0xd476a1c3461bbed2), UINT64_C(0xaeaed10b762e37ab),
+    UINT64_C(0x37deb6af5e9b8b5b), UINT64_C(0x4d06c6676eae0222),
+    UINT64_C(0xc26e573f3ef099a9), UINT64_C(0xb8b627f70ec510d0),
+    UINT64_C(0xf7e653dcc6da3dd4), UINT64_C(0x8d3e2314f6efb4ad),
+    UINT64_C(0x0256b24ca6b12f26), UINT64_C(0x788ec2849684a65f),
+    UINT64_C(0x9cf65a1b368f752e), UINT64_C(0xe62e2ad306bafc57),
+    UINT64_C(0x6946bb8b56e467dc), UINT64_C(0x139ecb4366d1eea5),
+    UINT64_C(0x5ccebf68aecec3a1), UINT64_C(0x2616cfa09efb4ad8),
+    UINT64_C(0xa97e5ef8cea5d153), UINT64_C(0xd3a62e30fe90582a),
+    UINT64_C(0xb0c7b7e3c7593bd8), UINT64_C(0xca1fc72bf76cb2a1),
+    UINT64_C(0x45775673a732292a), UINT64_C(0x3faf26bb9707a053),
+    UINT64_C(0x70ff52905f188d57), UINT64_C(0x0a2722586f2d042e),
+    UINT64_C(0x854fb3003f739fa5), UINT64_C(0xff97c3c80f4616dc),
+    UINT64_C(0x1bef5b57af4dc5ad), UINT64_C(0x61372b9f9f784cd4),
+    UINT64_C(0xee5fbac7cf26d75f), UINT64_C(0x9487ca0fff135e26),
+    UINT64_C(0xdbd7be24370c7322), UINT64_C(0xa10fceec0739fa5b),
+    UINT64_C(0x2e675fb4576761d0), UINT64_C(0x54bf2f7c6752e8a9),
+    UINT64_C(0xcdcf48d84fe75459), UINT64_C(0xb71738107fd2dd20),
+    UINT64_C(0x387fa9482f8c46ab), UINT64_C(0x42a7d9801fb9cfd2),
+    UINT64_C(0x0df7adabd7a6e2d6), UINT64_C(0x772fdd63e7936baf),
+    UINT64_C(0xf8474c3bb7cdf024), UINT64_C(0x829f3cf387f8795d),
+    UINT64_C(0x66e7a46c27f3aa2c), UINT64_C(0x1c3fd4a417c62355),
+    UINT64_C(0x935745fc4798b8de), UINT64_C(0xe98f353477ad31a7),
+    UINT64_C(0xa6df411fbfb21ca3), UINT64_C(0xdc0731d78f8795da),
+    UINT64_C(0x536fa08fdfd90e51), UINT64_C(0x29b7d047efec8728),
+};
+
+uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) {
+    uint64_t j;
+
+    for (j = 0; j < l; j++) {
+        uint8_t byte = s[j];
+        crc = crc64_tab[(uint8_t)crc ^ byte] ^ (crc >> 8);
+    }
+    return crc;
+}
+
+/* Test main */
+#ifdef TEST_MAIN
+#include <stdio.h>
+int main(void) {
+    printf("e9c6d914c4b8d9ca == %016llx\n",
+        (unsigned long long) crc64(0,(unsigned char*)"123456789",9));
+    return 0;
+}
+#endif
index 492e7aba2c4f7f5d727badefe352c76e744dc681..fff63f4846f86c88b0cc00db449931f71de97e7a 100644 (file)
--- a/src/db.c
+++ b/src/db.c
@@ -510,24 +510,23 @@ int expireIfNeeded(redisDb *db, robj *key) {
  * Expires Commands
  *----------------------------------------------------------------------------*/
 
-/* Given an string object return true if it contains exactly the "ms"
- * or "MS" string. This is used in order to check if the last argument
- * of EXPIRE, EXPIREAT or TTL is "ms" to switch into millisecond input/output */
-int stringObjectEqualsMs(robj *a) {
-    char *arg = a->ptr;
-    return tolower(arg[0]) == 'm' && tolower(arg[1]) == 's' && arg[2] == '\0';
-}
-
-void expireGenericCommand(redisClient *c, long long offset, int unit) {
+/* This is the generic command implementation for EXPIRE, PEXPIRE, EXPIREAT
+ * and PEXPIREAT. Because the commad second argument may be relative or absolute
+ * the "basetime" argument is used to signal what the base time is (either 0
+ * for *AT variants of the command, or the current time for relative expires).
+ *
+ * unit is either UNIT_SECONDS or UNIT_MILLISECONDS, and is only used for
+ * the argv[2] parameter. The basetime is always specified in milliesconds. */
+void expireGenericCommand(redisClient *c, long long basetime, int unit) {
     dictEntry *de;
     robj *key = c->argv[1], *param = c->argv[2];
-    long long milliseconds;
+    long long when; /* unix time in milliseconds when the key will expire. */
 
-    if (getLongLongFromObjectOrReply(c, param, &milliseconds, NULL) != REDIS_OK)
+    if (getLongLongFromObjectOrReply(c, param, &when, NULL) != REDIS_OK)
         return;
 
-    if (unit == UNIT_SECONDS) milliseconds *= 1000;
-    milliseconds -= offset;
+    if (unit == UNIT_SECONDS) when *= 1000;
+    when += basetime;
 
     de = dictFind(c->db->dict,key->ptr);
     if (de == NULL) {
@@ -540,7 +539,7 @@ void expireGenericCommand(redisClient *c, long long offset, int unit) {
      *
      * 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 (milliseconds <= 0 && !server.loading && !server.masterhost) {
+    if (when <= mstime() && !server.loading && !server.masterhost) {
         robj *aux;
 
         redisAssertWithInfo(c,key,dbDelete(c->db,key));
@@ -554,7 +553,6 @@ void expireGenericCommand(redisClient *c, long long offset, int unit) {
         addReply(c, shared.cone);
         return;
     } else {
-        long long when = mstime()+milliseconds;
         setExpire(c->db,key,when);
         addReply(c,shared.cone);
         signalModifiedKey(c->db,key);
@@ -564,19 +562,19 @@ void expireGenericCommand(redisClient *c, long long offset, int unit) {
 }
 
 void expireCommand(redisClient *c) {
-    expireGenericCommand(c,0,UNIT_SECONDS);
+    expireGenericCommand(c,mstime(),UNIT_SECONDS);
 }
 
 void expireatCommand(redisClient *c) {
-    expireGenericCommand(c,mstime(),UNIT_SECONDS);
+    expireGenericCommand(c,0,UNIT_SECONDS);
 }
 
 void pexpireCommand(redisClient *c) {
-    expireGenericCommand(c,0,UNIT_MILLISECONDS);
+    expireGenericCommand(c,mstime(),UNIT_MILLISECONDS);
 }
 
 void pexpireatCommand(redisClient *c) {
-    expireGenericCommand(c,mstime(),UNIT_MILLISECONDS);
+    expireGenericCommand(c,0,UNIT_MILLISECONDS);
 }
 
 void ttlGenericCommand(redisClient *c, int output_ms) {
index e7b3ba407fd756adadba96b932caf56f2aad9853..37e6a1f72c17f9ead483c18bdb63bfac964329cc 100644 (file)
@@ -105,7 +105,6 @@ void computeDatasetDigest(unsigned char *final) {
 
             mixDigest(digest,key,sdslen(key));
 
-            /* Make sure the key is loaded if VM is active */
             o = dictGetVal(de);
 
             aux = htonl(o->type);
index 6fd4584eede5041eb69ee257fbf74400b15accdd..2835de5668f4e7db43d5480c862d08c92a782cbd 100644 (file)
@@ -633,6 +633,21 @@ void dictEmpty(dict *d) {
     d->iterators = 0;
 }
 
+void dictEnableResize(void) {
+    dict_can_resize = 1;
+}
+
+void dictDisableResize(void) {
+    dict_can_resize = 0;
+}
+
+#if 0
+
+The following is code that we don't use for Redis currently, but that is part
+of the library.
+
+/* ----------------------- Debugging ------------------------*/
+
 #define DICT_STATS_VECTLEN 50
 static void _dictPrintStatsHt(dictht *ht) {
     unsigned long i, slots = 0, chainlen, maxchainlen = 0;
@@ -686,20 +701,6 @@ void dictPrintStats(dict *d) {
     }
 }
 
-void dictEnableResize(void) {
-    dict_can_resize = 1;
-}
-
-void dictDisableResize(void) {
-    dict_can_resize = 0;
-}
-
-#if 0
-
-/* The following are just example hash table types implementations.
- * Not useful for Redis so they are commented out.
- */
-
 /* ----------------------- StringCopy Hash Table Type ------------------------*/
 
 static unsigned int _dictStringCopyHTHashFunction(const void *key)
index 51613c9b3b42317144899116a4c079d14ba32e0d..cde95c1f8414d7c1415b9c4cd2b4cb166e8526db 100644 (file)
@@ -1,4 +1,4 @@
-/* Automatically generated by generate-command-help.rb, do not edit. */
+/* Automatically generated by utils/generate-command-help.rb, do not edit. */
 
 #ifndef __REDIS_HELP_H
 #define __REDIS_HELP_H
@@ -13,7 +13,8 @@ static char *commandGroups[] = {
     "pubsub",
     "transactions",
     "connection",
-    "server"
+    "server",
+    "scripting"
 };
 
 struct commandHelp {
@@ -27,612 +28,697 @@ struct commandHelp {
     "key value",
     "Append a value to a key",
     1,
-    "1.3.3" },
+    "2.0.0" },
     { "AUTH",
     "password",
     "Authenticate to the server",
     8,
-    "0.08" },
+    "1.0.0" },
     { "BGREWRITEAOF",
     "-",
     "Asynchronously rewrite the append-only file",
     9,
-    "1.07" },
+    "1.0.0" },
     { "BGSAVE",
     "-",
     "Asynchronously save the dataset to disk",
     9,
-    "0.07" },
+    "1.0.0" },
     { "BLPOP",
     "key [key ...] timeout",
     "Remove and get the first element in a list, or block until one is available",
     2,
-    "1.3.1" },
+    "2.0.0" },
     { "BRPOP",
     "key [key ...] timeout",
     "Remove and get the last element in a list, or block until one is available",
     2,
-    "1.3.1" },
+    "2.0.0" },
     { "BRPOPLPUSH",
     "source destination timeout",
     "Pop a value from a list, push it to another list and return it; or block until one is available",
     2,
-    "2.1.7" },
+    "2.2.0" },
     { "CONFIG GET",
     "parameter",
     "Get the value of a configuration parameter",
     9,
-    "2.0" },
+    "2.0.0" },
     { "CONFIG RESETSTAT",
     "-",
     "Reset the stats returned by INFO",
     9,
-    "2.0" },
+    "2.0.0" },
     { "CONFIG SET",
     "parameter value",
     "Set a configuration parameter to the given value",
     9,
-    "2.0" },
+    "2.0.0" },
     { "DBSIZE",
     "-",
     "Return the number of keys in the selected database",
     9,
-    "0.07" },
+    "1.0.0" },
     { "DEBUG OBJECT",
     "key",
     "Get debugging information about a key",
     9,
-    "0.101" },
+    "1.0.0" },
     { "DEBUG SEGFAULT",
     "-",
     "Make the server crash",
     9,
-    "0.101" },
+    "1.0.0" },
     { "DECR",
     "key",
     "Decrement the integer value of a key by one",
     1,
-    "0.07" },
+    "1.0.0" },
     { "DECRBY",
     "key decrement",
     "Decrement the integer value of a key by the given number",
     1,
-    "0.07" },
+    "1.0.0" },
     { "DEL",
     "key [key ...]",
     "Delete a key",
     0,
-    "0.07" },
+    "1.0.0" },
     { "DISCARD",
     "-",
     "Discard all commands issued after MULTI",
     7,
-    "1.3.3" },
+    "2.0.0" },
+    { "DUMP",
+    "key",
+    "Return a serialized verison of the value stored at the specified key.",
+    0,
+    "2.6.0" },
     { "ECHO",
     "message",
     "Echo the given string",
     8,
-    "0.07" },
+    "1.0.0" },
+    { "EVAL",
+    "script numkeys key [key ...] arg [arg ...]",
+    "Execute a Lua script server side",
+    10,
+    "2.6.0" },
     { "EXEC",
     "-",
     "Execute all commands issued after MULTI",
     7,
-    "1.1.95" },
+    "1.2.0" },
     { "EXISTS",
     "key",
     "Determine if a key exists",
-    9,
-    "0.07" },
+    0,
+    "1.0.0" },
     { "EXPIRE",
     "key seconds",
     "Set a key's time to live in seconds",
     0,
-    "0.09" },
+    "1.0.0" },
     { "EXPIREAT",
     "key timestamp",
     "Set the expiration for a key as a UNIX timestamp",
     0,
-    "1.1" },
+    "1.2.0" },
     { "FLUSHALL",
     "-",
     "Remove all keys from all databases",
     9,
-    "0.07" },
+    "1.0.0" },
     { "FLUSHDB",
     "-",
     "Remove all keys from the current database",
     9,
-    "0.07" },
+    "1.0.0" },
     { "GET",
     "key",
     "Get the value of a key",
     1,
-    "0.07" },
+    "1.0.0" },
     { "GETBIT",
     "key offset",
     "Returns the bit value at offset in the string value stored at key",
     1,
-    "2.1.8" },
+    "2.2.0" },
+    { "GETRANGE",
+    "key start end",
+    "Get a substring of the string stored at a key",
+    1,
+    "2.4.0" },
     { "GETSET",
     "key value",
     "Set the string value of a key and return its old value",
     1,
-    "0.091" },
+    "1.0.0" },
     { "HDEL",
-    "key field",
-    "Delete a hash field",
+    "key field [field ...]",
+    "Delete one or more hash fields",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "HEXISTS",
     "key field",
     "Determine if a hash field exists",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "HGET",
     "key field",
     "Get the value of a hash field",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "HGETALL",
     "key",
     "Get all the fields and values in a hash",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "HINCRBY",
     "key field increment",
     "Increment the integer value of a hash field by the given number",
     5,
-    "1.3.10" },
+    "2.0.0" },
+    { "HINCRBYFLOAT",
+    "key field increment",
+    "Increment the float value of a hash field by the given amount",
+    5,
+    "2.6.0" },
     { "HKEYS",
     "key",
     "Get all the fields in a hash",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "HLEN",
     "key",
     "Get the number of fields in a hash",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "HMGET",
     "key field [field ...]",
     "Get the values of all the given hash fields",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "HMSET",
     "key field value [field value ...]",
     "Set multiple hash fields to multiple values",
     5,
-    "1.3.8" },
+    "2.0.0" },
     { "HSET",
     "key field value",
     "Set the string value of a hash field",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "HSETNX",
     "key field value",
     "Set the value of a hash field, only if the field does not exist",
     5,
-    "1.3.8" },
+    "2.0.0" },
     { "HVALS",
     "key",
     "Get all the values in a hash",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "INCR",
     "key",
     "Increment the integer value of a key by one",
     1,
-    "0.07" },
+    "1.0.0" },
     { "INCRBY",
     "key increment",
-    "Increment the integer value of a key by the given number",
+    "Increment the integer value of a key by the given amount",
     1,
-    "0.07" },
+    "1.0.0" },
+    { "INCRBYFLOAT",
+    "key increment",
+    "Increment the float value of a key by the given amount",
+    1,
+    "2.6.0" },
     { "INFO",
     "-",
     "Get information and statistics about the server",
     9,
-    "0.07" },
+    "1.0.0" },
     { "KEYS",
     "pattern",
     "Find all keys matching the given pattern",
     0,
-    "0.07" },
+    "1.0.0" },
     { "LASTSAVE",
     "-",
     "Get the UNIX time stamp of the last successful save to disk",
     9,
-    "0.07" },
+    "1.0.0" },
     { "LINDEX",
     "key index",
     "Get an element from a list by its index",
     2,
-    "0.07" },
+    "1.0.0" },
     { "LINSERT",
     "key BEFORE|AFTER pivot value",
     "Insert an element before or after another element in a list",
     2,
-    "2.1.1" },
+    "2.2.0" },
     { "LLEN",
     "key",
     "Get the length of a list",
     2,
-    "0.07" },
+    "1.0.0" },
     { "LPOP",
     "key",
     "Remove and get the first element in a list",
     2,
-    "0.07" },
+    "1.0.0" },
     { "LPUSH",
-    "key value",
-    "Prepend a value to a list",
+    "key value [value ...]",
+    "Prepend one or multiple values to a list",
     2,
-    "0.07" },
+    "1.0.0" },
     { "LPUSHX",
     "key value",
     "Prepend a value to a list, only if the list exists",
     2,
-    "2.1.1" },
+    "2.2.0" },
     { "LRANGE",
     "key start stop",
     "Get a range of elements from a list",
     2,
-    "0.07" },
+    "1.0.0" },
     { "LREM",
     "key count value",
     "Remove elements from a list",
     2,
-    "0.07" },
+    "1.0.0" },
     { "LSET",
     "key index value",
     "Set the value of an element in a list by its index",
     2,
-    "0.07" },
+    "1.0.0" },
     { "LTRIM",
     "key start stop",
     "Trim a list to the specified range",
     2,
-    "0.07" },
+    "1.0.0" },
     { "MGET",
     "key [key ...]",
     "Get the values of all the given keys",
     1,
-    "0.07" },
+    "1.0.0" },
+    { "MIGRATE",
+    "host port key destination db timeout",
+    "Atomically transfer a key from a Redis instance to another one.",
+    0,
+    "2.6.0" },
     { "MONITOR",
     "-",
     "Listen for all requests received by the server in real time",
     9,
-    "0.07" },
+    "1.0.0" },
     { "MOVE",
     "key db",
     "Move a key to another database",
     0,
-    "0.07" },
+    "1.0.0" },
     { "MSET",
     "key value [key value ...]",
     "Set multiple keys to multiple values",
     1,
-    "1.001" },
+    "1.0.1" },
     { "MSETNX",
     "key value [key value ...]",
     "Set multiple keys to multiple values, only if none of the keys exist",
     1,
-    "1.001" },
+    "1.0.1" },
     { "MULTI",
     "-",
     "Mark the start of a transaction block",
     7,
-    "1.1.95" },
+    "1.2.0" },
+    { "OBJECT",
+    "subcommand [arguments [arguments ...]]",
+    "Inspect the internals of Redis objects",
+    0,
+    "2.2.3" },
     { "PERSIST",
     "key",
     "Remove the expiration from a key",
     0,
-    "2.1.2" },
+    "2.2.0" },
+    { "PEXPIRE",
+    "key milliseconds",
+    "Set a key's time to live in milliseconds",
+    0,
+    "2.6.0" },
+    { "PEXPIREAT",
+    "key milliseconds timestamp",
+    "Set the expiration for a key as a UNIX timestamp specified in milliseconds",
+    0,
+    "2.6.0" },
     { "PING",
     "-",
     "Ping the server",
     8,
-    "0.07" },
+    "1.0.0" },
+    { "PSETEX",
+    "key milliseconds value",
+    "Set the value and expiration in milliseconds of a key",
+    1,
+    "2.6.0" },
     { "PSUBSCRIBE",
-    "pattern",
+    "pattern [pattern ...]",
     "Listen for messages published to channels matching the given patterns",
     6,
-    "1.3.8" },
+    "2.0.0" },
+    { "PTTL",
+    "key",
+    "Get the time to live for a key in milliseconds",
+    0,
+    "2.6.0" },
     { "PUBLISH",
     "channel message",
     "Post a message to a channel",
     6,
-    "1.3.8" },
+    "2.0.0" },
     { "PUNSUBSCRIBE",
     "[pattern [pattern ...]]",
     "Stop listening for messages posted to channels matching the given patterns",
     6,
-    "1.3.8" },
+    "2.0.0" },
     { "QUIT",
     "-",
     "Close the connection",
     8,
-    "0.07" },
+    "1.0.0" },
     { "RANDOMKEY",
     "-",
     "Return a random key from the keyspace",
     0,
-    "0.07" },
+    "1.0.0" },
     { "RENAME",
     "key newkey",
     "Rename a key",
     0,
-    "0.07" },
+    "1.0.0" },
     { "RENAMENX",
     "key newkey",
     "Rename a key, only if the new key does not exist",
     0,
-    "0.07" },
+    "1.0.0" },
+    { "RESTORE",
+    "key ttl serialized value",
+    "Create a key using the provided serialized value, previously obtained using DUMP.",
+    0,
+    "2.6.0" },
     { "RPOP",
     "key",
     "Remove and get the last element in a list",
     2,
-    "0.07" },
+    "1.0.0" },
     { "RPOPLPUSH",
     "source destination",
     "Remove the last element in a list, append it to another list and return it",
     2,
-    "1.1" },
+    "1.2.0" },
     { "RPUSH",
-    "key value",
-    "Append a value to a list",
+    "key value [value ...]",
+    "Append one or multiple values to a list",
     2,
-    "0.07" },
+    "1.0.0" },
     { "RPUSHX",
     "key value",
     "Append a value to a list, only if the list exists",
     2,
-    "2.1.1" },
+    "2.2.0" },
     { "SADD",
-    "key member",
-    "Add a member to a set",
+    "key member [member ...]",
+    "Add one or more members to a set",
     3,
-    "0.07" },
+    "1.0.0" },
     { "SAVE",
     "-",
     "Synchronously save the dataset to disk",
     9,
-    "0.07" },
+    "1.0.0" },
     { "SCARD",
     "key",
     "Get the number of members in a set",
     3,
-    "0.07" },
+    "1.0.0" },
+    { "SCRIPT EXISTS",
+    "script [script ...]",
+    "Check existence of scripts in the script cache.",
+    10,
+    "2.6.0" },
+    { "SCRIPT FLUSH",
+    "-",
+    "Remove all the scripts from the script cache.",
+    10,
+    "2.6.0" },
+    { "SCRIPT KILL",
+    "-",
+    "Kill the script currently in execution.",
+    10,
+    "2.6.0" },
+    { "SCRIPT LOAD",
+    "script",
+    "Load the specified Lua script into the script cache.",
+    10,
+    "2.6.0" },
     { "SDIFF",
     "key [key ...]",
     "Subtract multiple sets",
     3,
-    "0.100" },
+    "1.0.0" },
     { "SDIFFSTORE",
     "destination key [key ...]",
     "Subtract multiple sets and store the resulting set in a key",
     3,
-    "0.100" },
+    "1.0.0" },
     { "SELECT",
     "index",
     "Change the selected database for the current connection",
     8,
-    "0.07" },
+    "1.0.0" },
     { "SET",
     "key value",
     "Set the string value of a key",
     1,
-    "0.07" },
+    "1.0.0" },
     { "SETBIT",
     "key offset value",
     "Sets or clears the bit at offset in the string value stored at key",
     1,
-    "2.1.8" },
+    "2.2.0" },
     { "SETEX",
     "key seconds value",
     "Set the value and expiration of a key",
     1,
-    "1.3.10" },
+    "2.0.0" },
     { "SETNX",
     "key value",
     "Set the value of a key, only if the key does not exist",
     1,
-    "0.07" },
+    "1.0.0" },
     { "SETRANGE",
     "key offset value",
     "Overwrite part of a string at key starting at the specified offset",
     1,
-    "2.1.8" },
+    "2.2.0" },
     { "SHUTDOWN",
-    "-",
+    "[NOSAVE] [SAVE]",
     "Synchronously save the dataset to disk and then shut down the server",
     9,
-    "0.07" },
+    "1.0.0" },
     { "SINTER",
     "key [key ...]",
     "Intersect multiple sets",
     3,
-    "0.07" },
+    "1.0.0" },
     { "SINTERSTORE",
     "destination key [key ...]",
     "Intersect multiple sets and store the resulting set in a key",
     3,
-    "0.07" },
+    "1.0.0" },
     { "SISMEMBER",
     "key member",
     "Determine if a given value is a member of a set",
     3,
-    "0.07" },
+    "1.0.0" },
     { "SLAVEOF",
     "host port",
     "Make the server a slave of another instance, or promote it as master",
     9,
-    "0.100" },
+    "1.0.0" },
+    { "SLOWLOG",
+    "subcommand [argument]",
+    "Manages the Redis slow queries log",
+    9,
+    "2.2.12" },
     { "SMEMBERS",
     "key",
     "Get all the members in a set",
     3,
-    "0.07" },
+    "1.0.0" },
     { "SMOVE",
     "source destination member",
     "Move a member from one set to another",
     3,
-    "0.091" },
+    "1.0.0" },
     { "SORT",
     "key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]",
     "Sort the elements in a list, set or sorted set",
     0,
-    "0.07" },
+    "1.0.0" },
     { "SPOP",
     "key",
     "Remove and return a random member from a set",
     3,
-    "0.101" },
+    "1.0.0" },
     { "SRANDMEMBER",
     "key",
     "Get a random member from a set",
     3,
-    "1.001" },
+    "1.0.0" },
     { "SREM",
-    "key member",
-    "Remove a member from a set",
+    "key member [member ...]",
+    "Remove one or more members from a set",
     3,
-    "0.07" },
+    "1.0.0" },
     { "STRLEN",
     "key",
     "Get the length of the value stored in a key",
     1,
-    "2.1.2" },
+    "2.2.0" },
     { "SUBSCRIBE",
-    "channel",
+    "channel [channel ...]",
     "Listen for messages published to the given channels",
     6,
-    "1.3.8" },
-    { "SUBSTR",
-    "key start end",
-    "Get a substring of the string stored at a key",
-    1,
-    "1.3.4" },
+    "2.0.0" },
     { "SUNION",
     "key [key ...]",
     "Add multiple sets",
     3,
-    "0.091" },
+    "1.0.0" },
     { "SUNIONSTORE",
     "destination key [key ...]",
     "Add multiple sets and store the resulting set in a key",
     3,
-    "0.091" },
+    "1.0.0" },
     { "SYNC",
     "-",
     "Internal command used for replication",
     9,
-    "0.07" },
+    "1.0.0" },
+    { "TIME",
+    "-",
+    "Return the current server time",
+    9,
+    "2.6.0" },
     { "TTL",
     "key",
     "Get the time to live for a key",
     0,
-    "0.100" },
+    "1.0.0" },
     { "TYPE",
     "key",
     "Determine the type stored at key",
     0,
-    "0.07" },
+    "1.0.0" },
     { "UNSUBSCRIBE",
     "[channel [channel ...]]",
     "Stop listening for messages posted to the given channels",
     6,
-    "1.3.8" },
+    "2.0.0" },
     { "UNWATCH",
     "-",
     "Forget about all watched keys",
     7,
-    "2.1.0" },
+    "2.2.0" },
     { "WATCH",
     "key [key ...]",
     "Watch the given keys to determine execution of the MULTI/EXEC block",
     7,
-    "2.1.0" },
+    "2.2.0" },
     { "ZADD",
-    "key score member",
-    "Add a member to a sorted set, or update its score if it already exists",
+    "key score member [score] [member]",
+    "Add one or more members to a sorted set, or update its score if it already exists",
     4,
-    "1.1" },
+    "1.2.0" },
     { "ZCARD",
     "key",
     "Get the number of members in a sorted set",
     4,
-    "1.1" },
+    "1.2.0" },
     { "ZCOUNT",
     "key min max",
     "Count the members in a sorted set with scores within the given values",
     4,
-    "1.3.3" },
+    "2.0.0" },
     { "ZINCRBY",
     "key increment member",
     "Increment the score of a member in a sorted set",
     4,
-    "1.1" },
+    "1.2.0" },
     { "ZINTERSTORE",
     "destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]",
     "Intersect multiple sorted sets and store the resulting sorted set in a new key",
     4,
-    "1.3.10" },
+    "2.0.0" },
     { "ZRANGE",
     "key start stop [WITHSCORES]",
     "Return a range of members in a sorted set, by index",
     4,
-    "1.1" },
+    "1.2.0" },
     { "ZRANGEBYSCORE",
     "key min max [WITHSCORES] [LIMIT offset count]",
     "Return a range of members in a sorted set, by score",
     4,
-    "1.050" },
+    "1.0.5" },
     { "ZRANK",
     "key member",
     "Determine the index of a member in a sorted set",
     4,
-    "1.3.4" },
+    "2.0.0" },
     { "ZREM",
-    "key member",
-    "Remove a member from a sorted set",
+    "key member [member ...]",
+    "Remove one or more members from a sorted set",
     4,
-    "1.1" },
+    "1.2.0" },
     { "ZREMRANGEBYRANK",
     "key start stop",
     "Remove all members in a sorted set within the given indexes",
     4,
-    "1.3.4" },
+    "2.0.0" },
     { "ZREMRANGEBYSCORE",
     "key min max",
     "Remove all members in a sorted set within the given scores",
     4,
-    "1.1" },
+    "1.2.0" },
     { "ZREVRANGE",
     "key start stop [WITHSCORES]",
     "Return a range of members in a sorted set, by index, with scores ordered from high to low",
     4,
-    "1.1" },
+    "1.2.0" },
     { "ZREVRANGEBYSCORE",
     "key max min [WITHSCORES] [LIMIT offset count]",
     "Return a range of members in a sorted set, by score, with scores ordered from high to low",
     4,
-    "2.1.6" },
+    "2.2.0" },
     { "ZREVRANK",
     "key member",
     "Determine the index of a member in a sorted set, with scores ordered from high to low",
     4,
-    "1.3.4" },
+    "2.0.0" },
     { "ZSCORE",
     "key member",
     "Get the score associated with the given member in a sorted set",
     4,
-    "1.1" },
+    "1.2.0" },
     { "ZUNIONSTORE",
     "destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]",
     "Add multiple sorted sets and store the resulting sorted set in a new key",
     4,
-    "1.3.10" }
+    "2.0.0" }
 };
 
 #endif
index 272ec502255527207afd27af952b222df5f10a24..09b4d831b1e579fa848af0887b1c7d013a04a16a 100644 (file)
 #endif
 
 #ifdef MEMTEST_32BIT
-#define ULONG_ONEZERO 0xaaaaaaaaaaaaaaaaUL
-#define ULONG_ZEROONE 0x5555555555555555UL
-#else
 #define ULONG_ONEZERO 0xaaaaaaaaUL
 #define ULONG_ZEROONE 0x55555555UL
+#else
+#define ULONG_ONEZERO 0xaaaaaaaaaaaaaaaaUL
+#define ULONG_ZEROONE 0x5555555555555555UL
 #endif
 
 static struct winsize ws;
@@ -47,7 +47,7 @@ void memtest_progress_end(void) {
 }
 
 void memtest_progress_step(size_t curr, size_t size, char c) {
-    size_t chars = (curr*progress_full)/size, j;
+    size_t chars = ((unsigned long long)curr*progress_full)/size, j;
 
     for (j = 0; j < chars-progress_printed; j++) {
         printf("%c",c);
index 75f6cf2213cb8b83f8488d01eadee13f85474d66..f922e2975133d0deaf438c633a06a03dddc41855 100644 (file)
@@ -1085,7 +1085,7 @@ sds getClientInfoString(redisClient *client) {
     if (emask & AE_WRITABLE) *p++ = 'w';
     *p = '\0';
     return sdscatprintf(sdsempty(),
-        "addr=%s:%d fd=%d age=%ld idle=%ld flags=%s db=%d sub=%d psub=%d qbuf=%lu qbuf-free=%lu obl=%lu oll=%lu omem=%lu events=%s cmd=%s",
+        "addr=%s:%d fd=%d age=%ld idle=%ld flags=%s db=%d sub=%d psub=%d multi=%d qbuf=%lu qbuf-free=%lu obl=%lu oll=%lu omem=%lu events=%s cmd=%s",
         ip,port,client->fd,
         (long)(server.unixtime - client->ctime),
         (long)(server.unixtime - client->lastinteraction),
@@ -1093,6 +1093,7 @@ sds getClientInfoString(redisClient *client) {
         client->db->id,
         (int) dictSize(client->pubsub_channels),
         (int) listLength(client->pubsub_patterns),
+        (client->flags & REDIS_MULTI) ? client->mstate.count : -1,
         (unsigned long) sdslen(client->querybuf),
         (unsigned long) sdsavail(client->querybuf),
         (unsigned long) client->bufpos,
index a15ebcfa8371fd25efcfa376a6cf58ad4e29bb0a..ba7ea323a6f2cbddd1f3cfafbc1cd93722fc6019 100644 (file)
@@ -9,18 +9,8 @@ robj *createObject(int type, void *ptr) {
     o->ptr = ptr;
     o->refcount = 1;
 
-    /* Set the LRU to the current lruclock (minutes resolution).
-     * We do this regardless of the fact VM is active as LRU is also
-     * used for the maxmemory directive when Redis is used as cache.
-     *
-     * Note that this code may run in the context of an I/O thread
-     * and accessing server.lruclock in theory is an error
-     * (no locks). But in practice this is safe, and even if we read
-     * garbage Redis will not fail. */
+    /* Set the LRU to the current lruclock (minutes resolution). */
     o->lru = server.lruclock;
-    /* The following is only needed if VM is active, but since the conditional
-     * is probably more costly than initializing the field it's better to
-     * have every field properly initialized anyway. */
     return o;
 }
 
index 1e23fa70cf392b399806ce9a7086af65114dd7c2..8ffd2c28f9d331f864d6aeb3be18ff50bc0b8fe3 100644 (file)
--- a/src/rdb.c
+++ b/src/rdb.c
@@ -1,6 +1,7 @@
 #include "redis.h"
 #include "lzf.h"    /* LZF compression library */
 #include "zipmap.h"
+#include "endianconv.h"
 
 #include <math.h>
 #include <sys/types.h>
@@ -26,11 +27,6 @@ int rdbLoadType(rio *rdb) {
     return type;
 }
 
-int rdbSaveTime(rio *rdb, time_t t) {
-    int32_t t32 = (int32_t) t;
-    return rdbWriteRaw(rdb,&t32,4);
-}
-
 time_t rdbLoadTime(rio *rdb) {
     int32_t t32;
     if (rioRead(rdb,&t32,4) == 0) return -1;
@@ -602,10 +598,12 @@ int rdbSave(char *filename) {
     dictIterator *di = NULL;
     dictEntry *de;
     char tmpfile[256];
+    char magic[10];
     int j;
     long long now = mstime();
     FILE *fp;
     rio rdb;
+    uint64_t cksum;
 
     snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
     fp = fopen(tmpfile,"w");
@@ -616,7 +614,10 @@ int rdbSave(char *filename) {
     }
 
     rioInitWithFile(&rdb,fp);
-    if (rdbWriteRaw(&rdb,"REDIS0004",9) == -1) goto werr;
+    if (server.rdb_checksum)
+        rdb.update_cksum = rioGenericUpdateChecksum;
+    snprintf(magic,sizeof(magic),"REDIS%04d",REDIS_RDB_VERSION);
+    if (rdbWriteRaw(&rdb,magic,9) == -1) goto werr;
 
     for (j = 0; j < server.dbnum; j++) {
         redisDb *db = server.db+j;
@@ -644,9 +645,17 @@ int rdbSave(char *filename) {
         }
         dictReleaseIterator(di);
     }
+    di = NULL; /* So that we don't release it again on error. */
+
     /* EOF opcode */
     if (rdbSaveType(&rdb,REDIS_RDB_OPCODE_EOF) == -1) goto werr;
 
+    /* CRC64 checksum. It will be zero if checksum computation is disabled, the
+     * loading code skips the check in this case. */
+    cksum = rdb.cksum;
+    memrev64ifbe(&cksum);
+    rioWrite(&rdb,&cksum,8);
+
     /* Make sure data will not remain on the OS's output buffers */
     fflush(fp);
     fsync(fileno(fp));
@@ -689,7 +698,7 @@ int rdbSaveBackground(char *filename) {
         if (server.ipfd > 0) close(server.ipfd);
         if (server.sofd > 0) close(server.sofd);
         retval = rdbSave(filename);
-        _exit((retval == REDIS_OK) ? 0 : 1);
+        exitFromChild((retval == REDIS_OK) ? 0 : 1);
     } else {
         /* Parent */
         server.stat_fork_time = ustime()-start;
@@ -720,7 +729,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
     size_t len;
     unsigned int i;
 
-    redisLog(REDIS_DEBUG,"LOADING OBJECT %d (at %d)\n",rdbtype,rdb->tell(rdb));
+    redisLog(REDIS_DEBUG,"LOADING OBJECT %d (at %d)\n",rdbtype,rioTell(rdb));
     if (rdbtype == REDIS_RDB_TYPE_STRING) {
         /* Read string value */
         if ((o = rdbLoadEncodedStringObject(rdb)) == NULL) return NULL;
@@ -1019,6 +1028,8 @@ int rdbLoad(char *filename) {
         return REDIS_ERR;
     }
     rioInitWithFile(&rdb,fp);
+    if (server.rdb_checksum)
+        rdb.update_cksum = rioGenericUpdateChecksum;
     if (rioRead(&rdb,buf,9) == 0) goto eoferr;
     buf[9] = '\0';
     if (memcmp(buf,"REDIS",5) != 0) {
@@ -1028,7 +1039,7 @@ int rdbLoad(char *filename) {
         return REDIS_ERR;
     }
     rdbver = atoi(buf+5);
-    if (rdbver < 1 || rdbver > 4) {
+    if (rdbver < 1 || rdbver > 5) {
         fclose(fp);
         redisLog(REDIS_WARNING,"Can't handle RDB format version %d",rdbver);
         errno = EINVAL;
@@ -1042,7 +1053,7 @@ int rdbLoad(char *filename) {
 
         /* Serve the clients from time to time */
         if (!(loops++ % 1000)) {
-            loadingProgress(rdb.tell(&rdb));
+            loadingProgress(rioTell(&rdb));
             aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT);
         }
 
@@ -1099,6 +1110,20 @@ int rdbLoad(char *filename) {
 
         decrRefCount(key);
     }
+    /* Verify the checksum if RDB version is >= 5 */
+    if (rdbver >= 5 && server.rdb_checksum) {
+        uint64_t cksum, expected = rdb.cksum;
+
+        if (rioRead(&rdb,&cksum,8) == 0) goto eoferr;
+        memrev64ifbe(&cksum);
+        if (cksum == 0) {
+            redisLog(REDIS_WARNING,"RDB file was saved with checksum disabled: no check performed.");
+        } else if (cksum != expected) {
+            redisLog(REDIS_WARNING,"Wrong RDB checksum. Aborting now.");
+            exit(1);
+        }
+    }
+
     fclose(fp);
     stopLoading();
     return REDIS_OK;
index 45beaa93a739332e7476576bbf90ed966bf94d5f..2be5d9cd11bf04888e6d28fa9674b801b1d1834f 100644 (file)
--- a/src/rdb.h
+++ b/src/rdb.h
@@ -7,6 +7,10 @@
 /* TBD: include only necessary headers. */
 #include "redis.h"
 
+/* The current RDB version. When the format changes in a way that is no longer
+ * backward compatible this number gets incremented. */
+#define REDIS_RDB_VERSION 5
+
 /* Defines related to the dump file format. To store 32 bits lengths for short
  * keys requires a lot of space, so we check the most significant 2 bits of
  * the first byte to interpreter the length:
index bdaa3964217d26fbeea21207c0ece2098f5d7868..3e0c58951a5beec7ca527bc91979ebe06b387fbb 100644 (file)
@@ -69,6 +69,7 @@ static struct config {
     int cluster_mode;
     int cluster_reissue_command;
     int slave_mode;
+    int bigkeys;
     int stdinarg; /* get last arg from stdin. (-x option) */
     char *auth;
     int output; /* output mode, see OUTPUT_* defines */
@@ -655,6 +656,8 @@ static int parseOptions(int argc, char **argv) {
             config.latency_mode = 1;
         } else if (!strcmp(argv[i],"--slave")) {
             config.slave_mode = 1;
+        } else if (!strcmp(argv[i],"--bigkeys")) {
+            config.bigkeys = 1;
         } else if (!strcmp(argv[i],"--eval") && !lastarg) {
             config.eval = argv[++i];
         } else if (!strcmp(argv[i],"-c")) {
@@ -711,6 +714,7 @@ static void usage() {
 "  --raw            Use raw formatting for replies (default when STDOUT is not a tty)\n"
 "  --latency        Enter a special mode continuously sampling latency.\n"
 "  --slave          Simulate a slave showing commands received from the master.\n"
+"  --bigkeys        Sample Redis keys looking for big keys.\n"
 "  --eval <file>    Send an EVAL command using the Lua script at <file>.\n"
 "  --help           Output this help and exit\n"
 "  --version        Output version and exit\n"
@@ -964,6 +968,87 @@ static void slaveMode(void) {
     while (cliReadReply(0) == REDIS_OK);
 }
 
+#define TYPE_STRING 0
+#define TYPE_LIST   1
+#define TYPE_SET    2
+#define TYPE_HASH   3
+#define TYPE_ZSET   4
+
+static void findBigKeys(void) {
+    unsigned long long biggest[5] = {0,0,0,0,0};
+    unsigned long long samples = 0;
+    redisReply *reply1, *reply2, *reply3;
+    char *sizecmd, *typename[] = {"string","list","set","hash","zset"};
+    int type;
+
+    printf("\n# Press ctrl+c when you have had enough of it... :)\n");
+    printf("# You can use -i 0.1 to sleep 0.1 sec every 100 sampled keys\n");
+    printf("# in order to reduce server load (usually not needed).\n\n");
+    while(1) {
+        /* Sample with RANDOMKEY */
+        reply1 = redisCommand(context,"RANDOMKEY");
+        if (reply1 == NULL) {
+            fprintf(stderr,"\nI/O error\n");
+            exit(1);
+        } else if (reply1->type == REDIS_REPLY_ERROR) {
+            fprintf(stderr, "RANDOMKEY error: %s\n",
+                reply1->str);
+            exit(1);
+        }
+        /* Get the key type */
+        reply2 = redisCommand(context,"TYPE %s",reply1->str);
+        assert(reply2 && reply2->type == REDIS_REPLY_STATUS);
+        samples++;
+
+        /* Get the key "size" */
+        if (!strcmp(reply2->str,"string")) {
+            sizecmd = "STRLEN";
+            type = TYPE_STRING;
+        } else if (!strcmp(reply2->str,"list")) {
+            sizecmd = "LLEN";
+            type = TYPE_LIST;
+        } else if (!strcmp(reply2->str,"set")) {
+            sizecmd = "SCARD";
+            type = TYPE_SET;
+        } else if (!strcmp(reply2->str,"hash")) {
+            sizecmd = "HLEN";
+            type = TYPE_HASH;
+        } else if (!strcmp(reply2->str,"zset")) {
+            sizecmd = "ZCARD";
+            type = TYPE_ZSET;
+        } else if (!strcmp(reply2->str,"none")) {
+            freeReplyObject(reply1);
+            freeReplyObject(reply2);
+            freeReplyObject(reply3);
+            continue;
+        } else {
+            fprintf(stderr, "Unknown key type '%s' for key '%s'\n",
+                reply2->str, reply1->str);
+            exit(1);
+        }
+
+        reply3 = redisCommand(context,"%s %s", sizecmd, reply1->str);
+        if (reply3 && reply3->type == REDIS_REPLY_INTEGER) {
+            if (biggest[type] < reply3->integer) {
+                printf("[%6s] %s | biggest so far with size %llu\n",
+                    typename[type], reply1->str,
+                    (unsigned long long) reply3->integer);
+                biggest[type] = reply3->integer;
+            }
+        }
+
+        if ((samples % 1000000) == 0)
+            printf("(%llu keys sampled)\n", samples);
+
+        if ((samples % 100) == 0 && config.interval)
+            usleep(config.interval);
+
+        freeReplyObject(reply1);
+        freeReplyObject(reply2);
+        if (reply3) freeReplyObject(reply3);
+    }
+}
+
 int main(int argc, char **argv) {
     int firstarg;
 
@@ -979,6 +1064,8 @@ int main(int argc, char **argv) {
     config.pubsub_mode = 0;
     config.latency_mode = 0;
     config.cluster_mode = 0;
+    config.slave_mode = 0;
+    config.bigkeys = 0;
     config.stdinarg = 0;
     config.auth = NULL;
     config.eval = NULL;
@@ -1005,6 +1092,12 @@ int main(int argc, char **argv) {
         slaveMode();
     }
 
+    /* Find big keys */
+    if (config.bigkeys) {
+        cliConnect(0);
+        findBigKeys();
+    }
+
     /* Start interactive mode when no command is provided */
     if (argc == 0 && !config.eval) {
         /* Note that in repl mode we don't abort on connection error.
index 473e492292912d60cbafbcaf917d24c61183b36a..1b83e19c2768c20d78f33c4f2d9f31a38f78630f 100755 (executable)
@@ -357,7 +357,7 @@ class RedisTrib
             keys = source.r.cluster("getkeysinslot",slot,10)
             break if keys.length == 0
             keys.each{|key|
-                source.r.migrate(target.info[:host],target.info[:port],key,0,1)
+                source.r.migrate(target.info[:host],target.info[:port],key,0,1000)
                 print "." if o[:verbose]
                 STDOUT.flush
             }
index 7ddeb886c216e7235db83b3629b4da34b58644ac..cb4883cc86e09878c948d4b0bf3d6f1ffed7725b 100644 (file)
@@ -48,6 +48,7 @@
 #include <float.h>
 #include <math.h>
 #include <sys/resource.h>
+#include <sys/utsname.h>
 
 /* Our shared "common" objects */
 
@@ -61,6 +62,9 @@ double R_Zero, R_PosInf, R_NegInf, R_Nan;
 
 /*================================= Globals ================================= */
 
+/* Alternate stack for SIGSEGV/etc handlers */
+char altstack[SIGSTKSZ];
+
 /* Global vars */
 struct redisServer server; /* server global state */
 struct redisCommand *commandTable;
@@ -222,7 +226,7 @@ struct redisCommand redisCommandTable[] = {
     {"ttl",ttlCommand,2,"r",0,NULL,1,1,1,0,0},
     {"pttl",pttlCommand,2,"r",0,NULL,1,1,1,0,0},
     {"persist",persistCommand,2,"w",0,NULL,1,1,1,0,0},
-    {"slaveof",slaveofCommand,3,"aws",0,NULL,0,0,0,0,0},
+    {"slaveof",slaveofCommand,3,"as",0,NULL,0,0,0,0,0},
     {"debug",debugCommand,-2,"as",0,NULL,0,0,0,0,0},
     {"config",configCommand,-2,"ar",0,NULL,0,0,0,0,0},
     {"subscribe",subscribeCommand,-2,"rps",0,NULL,0,0,0,0,0},
@@ -253,7 +257,6 @@ struct redisCommand redisCommandTable[] = {
 void redisLogRaw(int level, const char *msg) {
     const int syslogLevelMap[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING };
     const char *c = ".-*#";
-    time_t now = time(NULL);
     FILE *fp;
     char buf[64];
     int rawmode = (level & REDIS_LOG_RAW);
@@ -267,7 +270,12 @@ void redisLogRaw(int level, const char *msg) {
     if (rawmode) {
         fprintf(fp,"%s",msg);
     } else {
-        strftime(buf,sizeof(buf),"%d %b %H:%M:%S",localtime(&now));
+        int off;
+        struct timeval tv;
+
+        gettimeofday(&tv,NULL);
+        off = strftime(buf,sizeof(buf),"%d %b %H:%M:%S.",localtime(&tv.tv_sec));
+        snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000);
         fprintf(fp,"[%d] %s %c %s\n",(int)getpid(),buf,c[level],msg);
     }
     fflush(fp);
@@ -310,14 +318,15 @@ void redisLogFromHandler(int level, const char *msg) {
         STDOUT_FILENO;
     if (fd == -1) return;
     ll2string(buf,sizeof(buf),getpid());
-    write(fd,"[",1);
-    write(fd,buf,strlen(buf));
-    write(fd," | signal handler] (",20);
+    if (write(fd,"[",1) == -1) goto err;
+    if (write(fd,buf,strlen(buf)) == -1) goto err;
+    if (write(fd," | signal handler] (",20) == -1) goto err;
     ll2string(buf,sizeof(buf),time(NULL));
-    write(fd,buf,strlen(buf));
-    write(fd,") ",2);
-    write(fd,msg,strlen(msg));
-    write(fd,"\n",1);
+    if (write(fd,buf,strlen(buf)) == -1) goto err;
+    if (write(fd,") ",2) == -1) goto err;
+    if (write(fd,msg,strlen(msg)) == -1) goto err;
+    if (write(fd,"\n",1) == -1) goto err;
+err:
     if (server.logfile) close(fd);
 }
 
@@ -348,6 +357,18 @@ long long mstime(void) {
     return ustime()/1000;
 }
 
+/* After an RDB dump or AOF rewrite we exit from children using _exit() instead of
+ * exit(), because the latter may interact with the same file objects used by
+ * the parent process. However if we are testing the coverage normal exit() is
+ * used in order to obtain the right coverage information. */
+void exitFromChild(int retcode) {
+#ifdef COVERAGE_TEST
+    exit(retcode);
+#else
+    _exit(retcode);
+#endif
+}
+
 /*====================== Hash table type implementation  ==================== */
 
 /* This is an hash table type that uses the SDS dynamic strings libary as
@@ -977,16 +998,11 @@ void createSharedObjects(void) {
     shared.space = createObject(REDIS_STRING,sdsnew(" "));
     shared.colon = createObject(REDIS_STRING,sdsnew(":"));
     shared.plus = createObject(REDIS_STRING,sdsnew("+"));
-    shared.select0 = createStringObject("select 0\r\n",10);
-    shared.select1 = createStringObject("select 1\r\n",10);
-    shared.select2 = createStringObject("select 2\r\n",10);
-    shared.select3 = createStringObject("select 3\r\n",10);
-    shared.select4 = createStringObject("select 4\r\n",10);
-    shared.select5 = createStringObject("select 5\r\n",10);
-    shared.select6 = createStringObject("select 6\r\n",10);
-    shared.select7 = createStringObject("select 7\r\n",10);
-    shared.select8 = createStringObject("select 8\r\n",10);
-    shared.select9 = createStringObject("select 9\r\n",10);
+
+    for (j = 0; j < REDIS_SHARED_SELECT_CMDS; j++) {
+        shared.select[j] = createObject(REDIS_STRING,
+            sdscatprintf(sdsempty(),"select %d\r\n", j));
+    }
     shared.messagebulk = createStringObject("$7\r\nmessage\r\n",13);
     shared.pmessagebulk = createStringObject("$8\r\npmessage\r\n",14);
     shared.subscribebulk = createStringObject("$9\r\nsubscribe\r\n",15);
@@ -1046,6 +1062,7 @@ void initServerConfig() {
     server.aof_filename = zstrdup("appendonly.aof");
     server.requirepass = NULL;
     server.rdb_compression = 1;
+    server.rdb_checksum = 1;
     server.activerehashing = 1;
     server.maxclients = REDIS_MAX_CLIENTS;
     server.bpop_blocked_clients = 0;
@@ -1084,7 +1101,7 @@ void initServerConfig() {
     server.repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT;
     server.repl_serve_stale_data = 1;
     server.repl_slave_ro = 1;
-    server.repl_down_since = -1;
+    server.repl_down_since = time(NULL);
 
     /* Client output buffer limits */
     server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_NORMAL].hard_limit_bytes = 0;
@@ -1136,7 +1153,6 @@ void adjustOpenFilesLimit(void) {
     rlim_t maxfiles = server.maxclients+32;
     struct rlimit limit;
 
-    if (maxfiles < 1024) maxfiles = 1024;
     if (getrlimit(RLIMIT_NOFILE,&limit) == -1) {
         redisLog(REDIS_WARNING,"Unable to obtain the current NOFILE limit (%s), assuming 1024 and setting the max clients configuration accordingly.",
             strerror(errno));
@@ -1147,10 +1163,18 @@ void adjustOpenFilesLimit(void) {
         /* Set the max number of files if the current limit is not enough
          * for our needs. */
         if (oldlimit < maxfiles) {
-            limit.rlim_cur = maxfiles;
-            limit.rlim_max = maxfiles;
-            if (setrlimit(RLIMIT_NOFILE,&limit) == -1) {
-                server.maxclients = oldlimit-32;
+            rlim_t f;
+            
+            f = maxfiles;
+            while(f > oldlimit) {
+                limit.rlim_cur = f;
+                limit.rlim_max = f;
+                if (setrlimit(RLIMIT_NOFILE,&limit) != -1) break;
+                f -= 128;
+            }
+            if (f < oldlimit) f = oldlimit;
+            if (f != maxfiles) {
+                server.maxclients = f-32;
                 redisLog(REDIS_WARNING,"Unable to set the max number of files limit to %d (%s), setting the max clients configuration to %d.",
                     (int) maxfiles, strerror(errno), (int) server.maxclients);
             } else {
@@ -1575,7 +1599,7 @@ int processCommand(redisClient *c) {
 
     /* Lua script too slow? Only allow SHUTDOWN NOSAVE and SCRIPT KILL. */
     if (server.lua_timedout &&
-        !(c->cmd->proc != shutdownCommand &&
+        !(c->cmd->proc == shutdownCommand &&
           c->argc == 2 &&
           tolower(((char*)c->argv[1]->ptr)[0]) == 'n') &&
         !(c->cmd->proc == scriptCommand &&
@@ -1732,12 +1756,16 @@ sds genRedisInfoString(char *section) {
 
     /* Server */
     if (allsections || defsections || !strcasecmp(section,"server")) {
+        struct utsname name;
+
         if (sections++) info = sdscat(info,"\r\n");
+        uname(&name);
         info = sdscatprintf(info,
             "# Server\r\n"
             "redis_version:%s\r\n"
             "redis_git_sha1:%s\r\n"
             "redis_git_dirty:%d\r\n"
+            "os:%s %s %s\r\n"
             "arch_bits:%d\r\n"
             "multiplexing_api:%s\r\n"
             "gcc_version:%d.%d.%d\r\n"
@@ -1750,6 +1778,7 @@ sds genRedisInfoString(char *section) {
             REDIS_VERSION,
             redisGitSHA1(),
             strtol(redisGitDirty(),NULL,10) > 0,
+            name.sysname, name.release, name.machine,
             server.arch_bits,
             aeGetApiName(),
 #ifdef __GNUC__
@@ -1819,14 +1848,16 @@ sds genRedisInfoString(char *section) {
             "bgsave_in_progress:%d\r\n"
             "last_save_time:%ld\r\n"
             "last_bgsave_status:%s\r\n"
-            "bgrewriteaof_in_progress:%d\r\n",
+            "bgrewriteaof_in_progress:%d\r\n"
+            "bgrewriteaof_scheduled:%d\r\n",
             server.loading,
             server.aof_state != REDIS_AOF_OFF,
             server.dirty,
             server.rdb_child_pid != -1,
             server.lastsave,
             server.lastbgsave_status == REDIS_OK ? "ok" : "err",
-            server.aof_child_pid != -1);
+            server.aof_child_pid != -1,
+            server.aof_rewrite_scheduled);
 
         if (server.aof_state != REDIS_AOF_OFF) {
             info = sdscatprintf(info,
@@ -2010,7 +2041,7 @@ sds genRedisInfoString(char *section) {
         }
     }
 
-    /* Clusetr */
+    /* Cluster */
     if (allsections || defsections || !strcasecmp(section,"cluster")) {
         if (sections++) info = sdscat(info,"\r\n");
         info = sdscatprintf(info,
@@ -2270,8 +2301,12 @@ void daemonize(void) {
 }
 
 void version() {
-    printf("Redis server v=%s sha=%s:%d malloc=%s\n", REDIS_VERSION,
-        redisGitSHA1(), atoi(redisGitDirty()) > 0, ZMALLOC_LIB);
+    printf("Redis server v=%s sha=%s:%d malloc=%s bits=%d\n",
+        REDIS_VERSION,
+        redisGitSHA1(),
+        atoi(redisGitDirty()) > 0,
+        ZMALLOC_LIB,
+        sizeof(long) == 4 ? 32 : 64);
     exit(0);
 }
 
@@ -2316,15 +2351,24 @@ static void sigtermHandler(int sig) {
 
 void setupSignalHandlers(void) {
     struct sigaction act;
+    stack_t stack;
+
+    stack.ss_sp = altstack;
+    stack.ss_flags = 0;
+    stack.ss_size = SIGSTKSZ;
+
+    sigaltstack(&stack, NULL);
 
     /* When the SA_SIGINFO flag is set in sa_flags then sa_sigaction is used.
      * Otherwise, sa_handler is used. */
     sigemptyset(&act.sa_mask);
-    act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;
+    act.sa_flags = 0;
     act.sa_handler = sigtermHandler;
     sigaction(SIGTERM, &act, NULL);
 
 #ifdef HAVE_BACKTRACE
+    /* Use alternate stack so we don't clobber stack in case of segv, or when we run out of stack ..
+     * also resethand & nodefer so we can get interrupted (and killed) if we cause SEGV during SEGV handler */
     sigemptyset(&act.sa_mask);
     act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
     act.sa_sigaction = sigsegvHandler;
index 5a3d1a9c5f42f987f374b5f8bd74fec0ffa0f17f..42a9b5811b0d9ae85c0169677fb6308b90d200d4 100644 (file)
@@ -44,6 +44,7 @@
 #define REDIS_CONFIGLINE_MAX    1024
 #define REDIS_EXPIRELOOKUPS_PER_CRON    10 /* lookup 10 expires per loop */
 #define REDIS_MAX_WRITE_PER_EVENT (1024*64)
+#define REDIS_SHARED_SELECT_CMDS 10
 #define REDIS_SHARED_INTEGERS 10000
 #define REDIS_SHARED_BULKHDR_LEN 32
 #define REDIS_MAX_LOGMSG_LEN    1024 /* Default maximum length of syslog messages */
@@ -88,7 +89,6 @@
 #define REDIS_SET 2
 #define REDIS_ZSET 3
 #define REDIS_HASH 4
-#define REDIS_VMPOINTER 8
 
 /* Objects encoding. Some kind of objects like Strings and Hashes can be
  * internally represented in multiple ways. The 'encoding' field of the object
@@ -366,10 +366,9 @@ struct sharedObjectsStruct {
     *colon, *nullbulk, *nullmultibulk, *queued,
     *emptymultibulk, *wrongtypeerr, *nokeyerr, *syntaxerr, *sameobjecterr,
     *outofrangeerr, *noscripterr, *loadingerr, *slowscripterr, *bgsaveerr,
-    *roslaveerr, *oomerr, *plus, *select0, *select1, *select2, *select3,
-    *select4, *select5, *select6, *select7, *select8, *select9,
-    *messagebulk, *pmessagebulk, *subscribebulk, *unsubscribebulk,
-    *psubscribebulk, *punsubscribebulk, *del, *rpop, *lpop,
+    *roslaveerr, *oomerr, *plus, *messagebulk, *pmessagebulk, *subscribebulk,
+    *unsubscribebulk, *psubscribebulk, *punsubscribebulk, *del, *rpop, *lpop,
+    *select[REDIS_SHARED_SELECT_CMDS],
     *integers[REDIS_SHARED_INTEGERS],
     *mbulkhdr[REDIS_SHARED_BULKHDR_LEN], /* "*<value>\r\n" */
     *bulkhdr[REDIS_SHARED_BULKHDR_LEN];  /* "$<value>\r\n" */
@@ -647,6 +646,7 @@ struct redisServer {
     int saveparamslen;              /* Number of saving points */
     char *rdb_filename;             /* Name of RDB file */
     int rdb_compression;            /* Use compression in RDB? */
+    int rdb_checksum;               /* Use RDB checksum? */
     time_t lastsave;                /* Unix time of last save succeeede */
     int lastbgsave_status;          /* REDIS_OK or REDIS_ERR */
     int stop_writes_on_bgsave_err;  /* Don't allow writes if can't BGSAVE */
@@ -828,6 +828,8 @@ extern dictType hashDictType;
 long long ustime(void);
 long long mstime(void);
 void getRandomHexChars(char *p, unsigned int len);
+uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
+void exitFromChild(int retcode);
 
 /* networking.c -- Networking and Client related operations */
 redisClient *createClient(int fd);
@@ -945,9 +947,9 @@ int equalStringObjects(robj *a, robj *b);
 unsigned long estimateObjectIdleTime(robj *o);
 
 /* Synchronous I/O with timeout */
-int syncWrite(int fd, char *ptr, ssize_t size, int timeout);
-int syncRead(int fd, char *ptr, ssize_t size, int timeout);
-int syncReadLine(int fd, char *ptr, ssize_t size, int timeout);
+ssize_t syncWrite(int fd, char *ptr, ssize_t size, long long timeout);
+ssize_t syncRead(int fd, char *ptr, ssize_t size, long long timeout);
+ssize_t syncReadLine(int fd, char *ptr, ssize_t size, long long timeout);
 
 /* Replication */
 void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc);
index c99be32aff6c36be1878a06e5e8d83331a4a263f..ef148f1c28e20f2d6620d26f383392cde2e6e1f6 100644 (file)
@@ -25,24 +25,15 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
         if (slave->slaveseldb != dictid) {
             robj *selectcmd;
 
-            switch(dictid) {
-            case 0: selectcmd = shared.select0; break;
-            case 1: selectcmd = shared.select1; break;
-            case 2: selectcmd = shared.select2; break;
-            case 3: selectcmd = shared.select3; break;
-            case 4: selectcmd = shared.select4; break;
-            case 5: selectcmd = shared.select5; break;
-            case 6: selectcmd = shared.select6; break;
-            case 7: selectcmd = shared.select7; break;
-            case 8: selectcmd = shared.select8; break;
-            case 9: selectcmd = shared.select9; break;
-            default:
+            if (dictid >= 0 && dictid < REDIS_SHARED_SELECT_CMDS) {
+                selectcmd = shared.select[dictid];
+                incrRefCount(selectcmd);
+            } else {
                 selectcmd = createObject(REDIS_STRING,
                     sdscatprintf(sdsempty(),"select %d\r\n",dictid));
-                selectcmd->refcount = 0;
-                break;
             }
             addReply(slave,selectcmd);
+            decrRefCount(selectcmd);
             slave->slaveseldb = dictid;
         }
         addReplyMultiBulkLen(slave,argc);
@@ -291,7 +282,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
     /* If repl_transfer_left == -1 we still have to read the bulk length
      * from the master reply. */
     if (server.repl_transfer_left == -1) {
-        if (syncReadLine(fd,buf,1024,server.repl_syncio_timeout) == -1) {
+        if (syncReadLine(fd,buf,1024,server.repl_syncio_timeout*1000) == -1) {
             redisLog(REDIS_WARNING,
                 "I/O error reading bulk count from MASTER: %s",
                 strerror(errno));
@@ -414,13 +405,13 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
         size_t authlen;
 
         authlen = snprintf(authcmd,sizeof(authcmd),"AUTH %s\r\n",server.masterauth);
-        if (syncWrite(fd,authcmd,authlen,server.repl_syncio_timeout) == -1) {
+        if (syncWrite(fd,authcmd,authlen,server.repl_syncio_timeout*1000) == -1) {
             redisLog(REDIS_WARNING,"Unable to AUTH to MASTER: %s",
                 strerror(errno));
             goto error;
         }
         /* Read the AUTH result.  */
-        if (syncReadLine(fd,buf,1024,server.repl_syncio_timeout) == -1) {
+        if (syncReadLine(fd,buf,1024,server.repl_syncio_timeout*1000) == -1) {
             redisLog(REDIS_WARNING,"I/O error reading auth result from MASTER: %s",
                 strerror(errno));
             goto error;
@@ -432,7 +423,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
     }
 
     /* Issue the SYNC command */
-    if (syncWrite(fd,"SYNC \r\n",7,server.repl_syncio_timeout) == -1) {
+    if (syncWrite(fd,"SYNC \r\n",7,server.repl_syncio_timeout*1000) == -1) {
         redisLog(REDIS_WARNING,"I/O error writing to MASTER: %s",
             strerror(errno));
         goto error;
index 95b1ee7e6042f9f97e28691ffc9d95cee2fb3df3..44165d71565fa68009e2cd0a23b63c63076be2df 100644 (file)
--- a/src/rio.c
+++ b/src/rio.c
@@ -1,9 +1,25 @@
+/* rio.c is a simple stream-oriented I/O abstraction that provides an interface
+ * to write code that can consume/produce data using different concrete input
+ * and output devices. For instance the same rdb.c code using the rio abstraction
+ * can be used to read and write the RDB format using in-memory buffers or files.
+ *
+ * A rio object provides the following methods:
+ *  read: read from stream.
+ *  write: write to stream.
+ *  tell: get the current offset.
+ *
+ * It is also possible to set a 'checksum' method that is used by rio.c in order
+ * to compute a checksum of the data written or read, or to query the rio object
+ * for the current checksum. */
+
 #include "fmacros.h"
 #include <string.h>
 #include <stdio.h>
 #include "rio.h"
 #include "util.h"
 
+uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
+
 /* Returns 1 or 0 for success/failure. */
 static size_t rioBufferWrite(rio *r, const void *buf, size_t len) {
     r->io.buffer.ptr = sdscatlen(r->io.buffer.ptr,(char*)buf,len);
@@ -44,6 +60,8 @@ static const rio rioBufferIO = {
     rioBufferRead,
     rioBufferWrite,
     rioBufferTell,
+    NULL,           /* update_checksum */
+    0,              /* current checksum */
     { { NULL, 0 } } /* union for io-specific vars */
 };
 
@@ -51,6 +69,8 @@ static const rio rioFileIO = {
     rioFileRead,
     rioFileWrite,
     rioFileTell,
+    NULL,           /* update_checksum */
+    0,              /* current checksum */
     { { NULL, 0 } } /* union for io-specific vars */
 };
 
@@ -65,6 +85,16 @@ void rioInitWithBuffer(rio *r, sds s) {
     r->io.buffer.pos = 0;
 }
 
+/* This function can be installed both in memory and file streams when checksum
+ * computation is needed. */
+void rioGenericUpdateChecksum(rio *r, const void *buf, size_t len) {
+    r->cksum = crc64(r->cksum,buf,len);
+}
+
+/* ------------------------------ Higher level interface ---------------------------
+ * The following higher level functions use lower level rio.c functions to help
+ * generating the Redis protocol for the Append Only File. */
+
 /* Write multi bulk count in the format: "*<count>\r\n". */
 size_t rioWriteBulkCount(rio *r, char prefix, int count) {
     char cbuf[128];
index 2a830eb575aacb751f5b24e29f4a5d1cafaddf09..8befe6676e78a3ff269073964eea050259d7fed5 100644 (file)
--- a/src/rio.h
+++ b/src/rio.h
@@ -2,6 +2,7 @@
 #define __REDIS_RIO_H
 
 #include <stdio.h>
+#include <stdint.h>
 #include "sds.h"
 
 struct _rio {
@@ -11,6 +12,14 @@ struct _rio {
     size_t (*read)(struct _rio *, void *buf, size_t len);
     size_t (*write)(struct _rio *, const void *buf, size_t len);
     off_t (*tell)(struct _rio *);
+    /* The update_cksum method if not NULL is used to compute the checksum of all the
+     * data that was read or written so far. The method should be designed so that
+     * can be called with the current checksum, and the buf and len fields pointing
+     * to the new block of data to add to the checksum computation. */
+    void (*update_cksum)(struct _rio *, const void *buf, size_t len);
+
+    /* The current checksum */
+    uint64_t cksum;
 
     /* Backend-specific vars. */
     union {
@@ -26,8 +35,26 @@ struct _rio {
 
 typedef struct _rio rio;
 
-#define rioWrite(rio,buf,len) ((rio)->write((rio),(buf),(len)))
-#define rioRead(rio,buf,len) ((rio)->read((rio),(buf),(len)))
+/* The following functions are our interface with the stream. They'll call the
+ * actual implementation of read / write / tell, and will update the checksum
+ * if needed. */
+
+static inline size_t rioWrite(rio *r, const void *buf, size_t len) {
+    if (r->update_cksum) r->update_cksum(r,buf,len);
+    return r->write(r,buf,len);
+}
+
+static inline size_t rioRead(rio *r, void *buf, size_t len) {
+    if (r->read(r,buf,len) == 1) {
+        if (r->update_cksum) r->update_cksum(r,buf,len);
+        return 1;
+    }
+    return 0;
+}
+
+static inline off_t rioTell(rio *r) {
+    return r->tell(r);
+}
 
 void rioInitWithFile(rio *r, FILE *fp);
 void rioInitWithBuffer(rio *r, sds s);
@@ -37,4 +64,6 @@ size_t rioWriteBulkString(rio *r, const char *buf, size_t len);
 size_t rioWriteBulkLongLong(rio *r, long long l);
 size_t rioWriteBulkDouble(rio *r, double d);
 
+void rioGenericUpdateChecksum(rio *r, const void *buf, size_t len);
+
 #endif
index 0f876961a221cf4c200d1c4c76edba0615585a9e..4c7de33bf1d67c3b2038942fb467f9bb66b0ae77 100644 (file)
@@ -412,6 +412,43 @@ void luaLoadLibraries(lua_State *lua) {
 #endif
 }
 
+/* This function installs metamethods in the global table _G that prevent
+ * the creation of globals accidentally.
+ *
+ * It should be the last to be called in the scripting engine initialization
+ * sequence, because it may interact with creation of globals. */
+void scriptingEnableGlobalsProtection(lua_State *lua) {
+    char *s[32];
+    sds code = sdsempty();
+    int j = 0;
+
+    /* strict.lua from: http://metalua.luaforge.net/src/lib/strict.lua.html.
+     * Modified to be adapted to Redis. */
+    s[j++]="local mt = {}\n";
+    s[j++]="setmetatable(_G, mt)\n";
+    s[j++]="mt.__newindex = function (t, n, v)\n";
+    s[j++]="  if debug.getinfo(2) then\n";
+    s[j++]="    local w = debug.getinfo(2, \"S\").what\n";
+    s[j++]="    if w ~= \"main\" and w ~= \"C\" then\n";
+    s[j++]="      error(\"Script attempted to create global variable '\"..tostring(n)..\"'\", 2)\n";
+    s[j++]="    end\n";
+    s[j++]="  end\n";
+    s[j++]="  rawset(t, n, v)\n";
+    s[j++]="end\n";
+    s[j++]="mt.__index = function (t, n)\n";
+    s[j++]="  if debug.getinfo(2) and debug.getinfo(2, \"S\").what ~= \"C\" then\n";
+    s[j++]="    error(\"Script attempted to access unexisting global variable '\"..tostring(n)..\"'\", 2)\n";
+    s[j++]="  end\n";
+    s[j++]="  return rawget(t, n)\n";
+    s[j++]="end\n";
+    s[j++]=NULL;
+
+    for (j = 0; s[j] != NULL; j++) code = sdscatlen(code,s[j],strlen(s[j]));
+    luaL_loadbuffer(lua,code,sdslen(code),"@enable_strict_lua");
+    lua_pcall(lua,0,0,0);
+    sdsfree(code);
+}
+
 /* Initialize the scripting environment.
  * It is possible to call this function to reset the scripting environment
  * assuming that we call scriptingRelease() before.
@@ -488,7 +525,7 @@ void scriptingInit(void) {
                                 "  if b == false then b = '' end\n"
                                 "  return a<b\n"
                                 "end\n";
-        luaL_loadbuffer(lua,compare_func,strlen(compare_func),"cmp_func_def");
+        luaL_loadbuffer(lua,compare_func,strlen(compare_func),"@cmp_func_def");
         lua_pcall(lua,0,0,0);
     }
 
@@ -501,6 +538,11 @@ void scriptingInit(void) {
         server.lua_client->flags |= REDIS_LUA_CLIENT;
     }
 
+    /* Lua beginners ofter don't use "local", this is likely to introduce
+     * subtle bugs in their code. To prevent problems we protect accesses
+     * to global variables. */
+    scriptingEnableGlobalsProtection(lua);
+
     server.lua = lua;
 }
 
@@ -634,7 +676,7 @@ int luaCreateFunction(redisClient *c, lua_State *lua, char *funcname, robj *body
     funcdef = sdscatlen(funcdef,body->ptr,sdslen(body->ptr));
     funcdef = sdscatlen(funcdef," end",4);
 
-    if (luaL_loadbuffer(lua,funcdef,sdslen(funcdef),"func definition")) {
+    if (luaL_loadbuffer(lua,funcdef,sdslen(funcdef),"@user_script")) {
         addReplyErrorFormat(c,"Error compiling script (new function): %s\n",
             lua_tostring(lua,-1));
         lua_pop(lua,1);
index 3f02e49a709a4cbf32c6d4ba92480de55d218f05..c1ed5517f2fc00d4edbf453445cfcc4d5d9f7b76 100644 (file)
@@ -9,21 +9,27 @@ redisSortOperation *createSortOperation(int type, robj *pattern) {
     return so;
 }
 
-/* Return the value associated to the key with a name obtained
- * substituting the first occurence of '*' in 'pattern' with 'subst'.
+/* Return the value associated to the key with a name obtained using
+ * the following rules:
+ *
+ * 1) The first occurence of '*' in 'pattern' is substituted with 'subst'.
+ *
+ * 2) If 'pattern' matches the "->" string, everything on the left of
+ *    the arrow is treated as the name of an hash field, and the part on the
+ *    left as the key name containing an hash. The value of the specified
+ *    field is returned.
+ *
+ * 3) If 'pattern' equals "#", the function simply returns 'subst' itself so
+ *    that the SORT command can be used like: SORT key GET # to retrieve
+ *    the Set/List elements directly.
+ *
  * The returned object will always have its refcount increased by 1
  * when it is non-NULL. */
 robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {
-    char *p, *f;
+    char *p, *f, *k;
     sds spat, ssub;
-    robj keyobj, fieldobj, *o;
+    robj *keyobj, *fieldobj = NULL, *o;
     int prefixlen, sublen, postfixlen, fieldlen;
-    /* Expoit the internal sds representation to create a sds string allocated on the stack in order to make this function faster */
-    struct {
-        int len;
-        int free;
-        char buf[REDIS_SORTKEY_MAX+1];
-    } keyname, fieldname;
 
     /* If the pattern is "#" return the substitution object itself in order
      * to implement the "SORT ... GET #" feature. */
@@ -37,9 +43,10 @@ robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {
      * a decoded object on the fly. Otherwise getDecodedObject will just
      * increment the ref count, that we'll decrement later. */
     subst = getDecodedObject(subst);
-
     ssub = subst->ptr;
-    if (sdslen(spat)+sdslen(ssub)-1 > REDIS_SORTKEY_MAX) return NULL;
+
+    /* If we can't find '*' in the pattern we return NULL as to GET a
+     * fixed key does not make sense. */
     p = strchr(spat,'*');
     if (!p) {
         decrRefCount(subst);
@@ -47,46 +54,49 @@ robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {
     }
 
     /* Find out if we're dealing with a hash dereference. */
-    if ((f = strstr(p+1, "->")) != NULL) {
-        fieldlen = sdslen(spat)-(f-spat);
-        /* this also copies \0 character */
-        memcpy(fieldname.buf,f+2,fieldlen-1);
-        fieldname.len = fieldlen-2;
+    if ((f = strstr(p+1, "->")) != NULL && *(f+2) != '\0') {
+        fieldlen = sdslen(spat)-(f-spat)-2;
+        fieldobj = createStringObject(f+2,fieldlen);
     } else {
         fieldlen = 0;
     }
 
+    /* Perform the '*' substitution. */
     prefixlen = p-spat;
     sublen = sdslen(ssub);
-    postfixlen = sdslen(spat)-(prefixlen+1)-fieldlen;
-    memcpy(keyname.buf,spat,prefixlen);
-    memcpy(keyname.buf+prefixlen,ssub,sublen);
-    memcpy(keyname.buf+prefixlen+sublen,p+1,postfixlen);
-    keyname.buf[prefixlen+sublen+postfixlen] = '\0';
-    keyname.len = prefixlen+sublen+postfixlen;
-    decrRefCount(subst);
+    postfixlen = sdslen(spat)-(prefixlen+1)-(fieldlen ? fieldlen+2 : 0);
+    keyobj = createStringObject(NULL,prefixlen+sublen+postfixlen);
+    k = keyobj->ptr;
+    memcpy(k,spat,prefixlen);
+    memcpy(k+prefixlen,ssub,sublen);
+    memcpy(k+prefixlen+sublen,p+1,postfixlen);
+    decrRefCount(subst); /* Incremented by decodeObject() */
 
     /* Lookup substituted key */
-    initStaticStringObject(keyobj,((char*)&keyname)+(sizeof(struct sdshdr)));
-    o = lookupKeyRead(db,&keyobj);
-    if (o == NULL) return NULL;
+    o = lookupKeyRead(db,keyobj);
+    if (o == NULL) goto noobj;
 
-    if (fieldlen > 0) {
-        if (o->type != REDIS_HASH || fieldname.len < 1) return NULL;
+    if (fieldobj) {
+        if (o->type != REDIS_HASH) goto noobj;
 
         /* Retrieve value from hash by the field name. This operation
          * already increases the refcount of the returned object. */
-        initStaticStringObject(fieldobj,((char*)&fieldname)+(sizeof(struct sdshdr)));
-        o = hashTypeGetObject(o, &fieldobj);
+        o = hashTypeGetObject(o, fieldobj);
     } else {
-        if (o->type != REDIS_STRING) return NULL;
+        if (o->type != REDIS_STRING) goto noobj;
 
         /* Every object that this function returns needs to have its refcount
          * increased. sortCommand decreases it again. */
         incrRefCount(o);
     }
-
+    decrRefCount(keyobj);
+    if (fieldobj) decrRefCount(fieldobj);
     return o;
+
+noobj:
+    decrRefCount(keyobj);
+    if (fieldlen) decrRefCount(fieldobj);
+    return NULL;
 }
 
 /* sortCompare() is used by qsort in sortCommand(). Given that qsort_r with
index 9958363be23382c4bec7e520ce8a22d20a480271..b0c2969e2cc4188a690cef2edd5bbb810eec615e 100644 (file)
  * of the SYNC command where the slave does it in a blocking way, and
  * the MIGRATE command that must be blocking in order to be atomic from the
  * point of view of the two instances (one migrating the key and one receiving
- * the key). This is why need the following blocking I/O functions. */
+ * the key). This is why need the following blocking I/O functions.
+ *
+ * All the functions take the timeout in milliseconds. */
+
+#define REDIS_SYNCIO_RESOLUTION 10 /* Resolution in milliseconds */
 
-int syncWrite(int fd, char *ptr, ssize_t size, int timeout) {
+/* Write the specified payload to 'fd'. If writing the whole payload will be done
+ * within 'timeout' milliseconds the operation succeeds and 'size' is returned.
+ * Otherwise the operation fails, -1 is returned, and an unspecified partial write
+ * could be performed against the file descriptor. */
+ssize_t syncWrite(int fd, char *ptr, ssize_t size, long long timeout) {
     ssize_t nwritten, ret = size;
-    time_t start = time(NULL);
+    long long start = mstime();
+    long long remaining = timeout;
 
-    timeout++;
-    while(size) {
-        if (aeWait(fd,AE_WRITABLE,1000) & AE_WRITABLE) {
+    while(1) {
+        long long wait = (remaining > REDIS_SYNCIO_RESOLUTION) ?
+                          remaining : REDIS_SYNCIO_RESOLUTION;
+        long long elapsed;
+
+        if (aeWait(fd,AE_WRITABLE,wait) & AE_WRITABLE) {
             nwritten = write(fd,ptr,size);
             if (nwritten == -1) return -1;
             ptr += nwritten;
             size -= nwritten;
+            if (size == 0) return ret;
         }
-        if ((time(NULL)-start) > timeout) {
+        elapsed = mstime() - start;
+        if (elapsed >= timeout) {
             errno = ETIMEDOUT;
             return -1;
         }
+        remaining = timeout - elapsed;
     }
-    return ret;
 }
 
-int syncRead(int fd, char *ptr, ssize_t size, int timeout) {
+/* Read the specified amount of bytes from 'fd'. If all the bytes are read within
+ * 'timeout' milliseconds the operation succeed and 'size' is returned.
+ * Otherwise the operation fails, -1 is returned, and an unspecified amount of
+ * data could be read from the file descriptor. */
+ssize_t syncRead(int fd, char *ptr, ssize_t size, long long timeout) {
     ssize_t nread, totread = 0;
-    time_t start = time(NULL);
+    long long start = mstime();
+    long long remaining = timeout;
 
-    timeout++;
-    while(size) {
-        if (aeWait(fd,AE_READABLE,1000) & AE_READABLE) {
+    while(1) {
+        long long wait = (remaining > REDIS_SYNCIO_RESOLUTION) ?
+                          remaining : REDIS_SYNCIO_RESOLUTION;
+        long long elapsed;
+
+        if (aeWait(fd,AE_READABLE,wait) & AE_READABLE) {
             nread = read(fd,ptr,size);
             if (nread <= 0) return -1;
             ptr += nread;
             size -= nread;
             totread += nread;
+            if (size == 0) return totread;
         }
-        if ((time(NULL)-start) > timeout) {
+        elapsed = mstime() - start;
+        if (elapsed >= timeout) {
             errno = ETIMEDOUT;
             return -1;
         }
+        remaining = timeout - elapsed;
     }
-    return totread;
 }
 
-int syncReadLine(int fd, char *ptr, ssize_t size, int timeout) {
+/* Read a line making sure that every char will not require more than 'timeout'
+ * milliseconds to be read.
+ * 
+ * On success the number of bytes read is returned, otherwise -1.
+ * On success the string is always correctly terminated with a 0 byte. */
+ssize_t syncReadLine(int fd, char *ptr, ssize_t size, long long timeout) {
     ssize_t nread = 0;
 
     size--;
index 6a16a63201921028c4b13014d8039238d1be599c..ca03916b953fd1eb91d9e80b506b6646d97e45f4 100644 (file)
@@ -699,6 +699,8 @@ void rpoplpushCommand(redisClient *c) {
         checkType(c,sobj,REDIS_LIST)) return;
 
     if (listTypeLength(sobj) == 0) {
+        /* This may only happen after loading very old RDB files. Recent
+         * versions of Redis delete keys of empty lists. */
         addReply(c,shared.nullbulk);
     } else {
         robj *dobj = lookupKeyWrite(c->db,c->argv[2]);
index 9ef74b080257dc636a8283b6570509ca53023cbc..08f8649fc6fd75e804d379700a534708c439a6d0 100644 (file)
@@ -1 +1 @@
-#define REDIS_VERSION "2.9.5"
+#define REDIS_VERSION "2.9.7"
index 8079fb5319a0e568b8d64e0bc44c3c5117e3614b..ff555619e5f811edbdc37413ca1f782616570794 100644 (file)
@@ -38,7 +38,7 @@
 #if defined(USE_TCMALLOC)
 #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR))
 #include <google/tcmalloc.h>
-#if TC_VERSION_MAJOR >= 1 && TC_VERSION_MINOR >= 6
+#if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1)
 #define HAVE_MALLOC_SIZE 1
 #define zmalloc_size(p) tc_malloc_size(p)
 #else
@@ -49,7 +49,7 @@
 #define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX))
 #define JEMALLOC_MANGLE
 #include <jemalloc/jemalloc.h>
-#if JEMALLOC_VERSION_MAJOR >= 2 && JEMALLOC_VERSION_MINOR >= 1
+#if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)
 #define HAVE_MALLOC_SIZE 1
 #define zmalloc_size(p) JEMALLOC_P(malloc_usable_size)(p)
 #else
diff --git a/tests/helpers/bg_complex_data.tcl b/tests/helpers/bg_complex_data.tcl
new file mode 100644 (file)
index 0000000..dffd7c6
--- /dev/null
@@ -0,0 +1,10 @@
+source tests/support/redis.tcl
+source tests/support/util.tcl
+
+proc bg_complex_data {host port db ops} {
+    set r [redis $host $port]
+    $r select $db
+    createComplexDataset $r $ops
+}
+
+bg_complex_data [lindex $argv 0] [lindex $argv 1] [lindex $argv 2] [lindex $argv 3]
diff --git a/tests/integration/replication-4.tcl b/tests/integration/replication-4.tcl
new file mode 100644 (file)
index 0000000..69fcab3
--- /dev/null
@@ -0,0 +1,54 @@
+proc start_bg_complex_data {host port db ops} {
+    exec tclsh8.5 tests/helpers/bg_complex_data.tcl $host $port $db $ops &
+}
+
+proc stop_bg_complex_data {handle} {
+    catch {exec /bin/kill -9 $handle}
+}
+
+start_server {tags {"repl"}} {
+    start_server {} {
+
+        set master [srv 0 client]
+        set master_host [srv 0 host]
+        set master_port [srv 0 port]
+        set load_handle0 [start_bg_complex_data $master_host $master_port 9 100000]
+        set load_handle1 [start_bg_complex_data $master_host $master_port 11 100000]
+        set load_handle2 [start_bg_complex_data $master_host $master_port 12 100000]
+
+        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 {Test replication with parallel clients writing in differnet DBs} {
+            lappend slave [srv 0 client]
+            after 5000
+            stop_bg_complex_data $load_handle0
+            stop_bg_complex_data $load_handle1
+            stop_bg_complex_data $load_handle2
+            set retry 10
+            while {$retry && ([$master debug digest] ne [$slave debug digest])}\
+            {
+                after 1000
+                incr retry -1
+            }
+            assert {[$master dbsize] > 0}
+
+            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]
+        }
+    }
+}
index 2c7d98deaa09a679a2e8b744ac626b4030d092e2..7c1edb550a2ae0a65095610fca3259e529e41ea0 100644 (file)
@@ -97,7 +97,7 @@ start_server {tags {"repl"}} {
                     [lindex $slaves 2] slaveof $master_host $master_port
 
                     # Wait for all the three slaves to reach the "online" state
-                    set retry 100
+                    set retry 500
                     while {$retry} {
                         set info [r -3 info]
                         if {[string match {*slave0:*,online*slave1:*,online*slave2:*,online*} $info]} {
index ca6cf34b601eb83d57b66415e5cd71779c518491..99415b6409e91272862f696ac9829a894db305a5 100644 (file)
@@ -142,9 +142,15 @@ proc ::redis::redis_multi_bulk_read fd {
     set count [redis_read_line $fd]
     if {$count == -1} return {}
     set l {}
+    set err {}
     for {set i 0} {$i < $count} {incr i} {
-        lappend l [redis_read_reply $fd]
+        if {[catch {
+            lappend l [redis_read_reply $fd]
+        } e] && $err eq {}} {
+            set err $e
+        }
     }
+    if {$err ne {}} {return -code error $err}
     return $l
 }
 
index 984270adb1b47e803d4e44825b3114df08423792..b1ab38fc1089afcfae386c14d16f5ff041fecc3f 100644 (file)
@@ -46,11 +46,16 @@ proc kill_server config {
     }
 
     # kill server and wait for the process to be totally exited
+    catch {exec kill $pid}
     while {[is_alive $config]} {
-        if {[incr wait 10] % 1000 == 0} {
+        incr wait 10
+
+        if {$wait >= 5000} {
+            puts "Forcing process $pid to exit..."
+            catch {exec kill -KILL $pid}
+        } elseif {$wait % 1000 == 0} {
             puts "Waiting for process $pid to exit..."
         }
-        catch {exec kill $pid}
         after 10
     }
 
index bef8231a71a0d020bd2cc5fabb3887ed3fe976bf..598a392916538a1900cb711dd3ec31668444bb64 100644 (file)
@@ -29,6 +29,7 @@ set ::all_tests {
     integration/replication
     integration/replication-2
     integration/replication-3
+    integration/replication-4
     integration/aof
     integration/rdb
     integration/convert-zipmap-hash-on-load
@@ -37,7 +38,9 @@ set ::all_tests {
     unit/scripting
     unit/maxmemory
     unit/introspection
+    unit/limits
     unit/obuf-limits
+    unit/dump
 }
 # Index to the next test to run in the ::all_tests list.
 set ::next_test 0
@@ -343,6 +346,7 @@ proc print_help_screen {} {
         "--quiet            Don't show individual tests."
         "--single <unit>    Just execute the specified unit (see next option)."
         "--list-tests       List all the available test units."
+        "--clients <num>    Number of test clients (16)."
         "--force-failure    Force the execution of a test that always fails."
         "--help             Print this help screen."
     } "\n"]
@@ -388,6 +392,9 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
         set ::client 1
         set ::test_server_port $arg
         incr j
+    } elseif {$opt eq {--clients}} {
+        set ::numclients $arg
+        incr j
     } elseif {$opt eq {--help}} {
         print_help_screen
         exit 0
index 358266ef78059eb56a0300d3f3f46ee351859a4b..e341d77b215159e1e47867b140387bb809cce2c2 100644 (file)
@@ -1,4 +1,15 @@
 start_server {tags {"aofrw"}} {
+
+    test {Turning off AOF kills the background writing child if any} {
+        r config set appendonly yes
+        waitForBgrewriteaof r
+        r multi
+        r bgrewriteaof
+        r config set appendonly no
+        r exec
+        set result [exec cat [srv 0 stdout] | tail -n1]
+    } {*Killing*AOF*child*}
+
     foreach d {string int} {
         foreach e {ziplist linkedlist} {
             test "AOF rewrite of list with $e encoding, $d data" {
@@ -104,4 +115,30 @@ start_server {tags {"aofrw"}} {
             }
         }
     }
+
+    test {BGREWRITEAOF is delayed if BGSAVE is in progress} {
+        r multi
+        r bgsave
+        r bgrewriteaof
+        r info persistence
+        set res [r exec]
+        assert_match {*scheduled*} [lindex $res 1]
+        assert_match {*bgrewriteaof_scheduled:1*} [lindex $res 2]
+        while {[string match {*bgrewriteaof_scheduled:1*} [r info persistence]]} {
+            after 100
+        }
+    }
+
+    test {BGREWRITEAOF is refused if already in progress} {
+        catch {
+            r multi
+            r bgrewriteaof
+            r bgrewriteaof
+            r exec
+        } e
+        assert_match {*ERR*already*} $e
+        while {[string match {*bgrewriteaof_scheduled:1*} [r info persistence]]} {
+            after 100
+        }
+    }
 }
diff --git a/tests/unit/dump.tcl b/tests/unit/dump.tcl
new file mode 100644 (file)
index 0000000..b73cde0
--- /dev/null
@@ -0,0 +1,112 @@
+start_server {tags {"dump"}} {
+    test {DUMP / RESTORE are able to serialize / unserialize a simple key} {
+        r set foo bar
+        set encoded [r dump foo]
+        r del foo
+        list [r exists foo] [r restore foo 0 $encoded] [r ttl foo] [r get foo]
+    } {0 OK -1 bar}
+
+    test {RESTORE can set an arbitrary expire to the materialized key} {
+        r set foo bar
+        set encoded [r dump foo]
+        r del foo
+        r restore foo 5000 $encoded
+        set ttl [r pttl foo]
+        assert {$ttl >= 3000 && $ttl <= 5000}
+        r get foo
+    } {bar}
+
+    test {RESTORE returns an error of the key already exists} {
+        r set foo bar
+        set e {}
+        catch {r restore foo 0 "..."} e
+        set e
+    } {*is busy*}
+
+    test {DUMP of non existing key returns nil} {
+        r dump nonexisting_key
+    } {}
+
+    test {MIGRATE is able to migrate a key between two instances} {
+        set first [srv 0 client]
+        r set key "Some Value"
+        start_server {tags {"repl"}} {
+            set second [srv 0 client]
+            set second_host [srv 0 host]
+            set second_port [srv 0 port]
+
+            assert {[$first exists key] == 1}
+            assert {[$second exists key] == 0}
+            set ret [r -1 migrate $second_host $second_port key 9 5000]
+            assert {$ret eq {OK}}
+            assert {[$first exists key] == 0}
+            assert {[$second exists key] == 1}
+            assert {[$second get key] eq {Some Value}}
+            assert {[$second ttl key] == -1}
+        }
+    }
+
+    test {MIGRATE propagates TTL correctly} {
+        set first [srv 0 client]
+        r set key "Some Value"
+        start_server {tags {"repl"}} {
+            set second [srv 0 client]
+            set second_host [srv 0 host]
+            set second_port [srv 0 port]
+
+            assert {[$first exists key] == 1}
+            assert {[$second exists key] == 0}
+            $first expire key 10
+            set ret [r -1 migrate $second_host $second_port key 9 5000]
+            assert {$ret eq {OK}}
+            assert {[$first exists key] == 0}
+            assert {[$second exists key] == 1}
+            assert {[$second get key] eq {Some Value}}
+            assert {[$second ttl key] >= 7 && [$second ttl key] <= 10}
+        }
+    }
+
+    test {MIGRATE can correctly transfer large values} {
+        set first [srv 0 client]
+        r del key
+        for {set j 0} {$j < 5000} {incr j} {
+            r rpush key 1 2 3 4 5 6 7 8 9 10
+            r rpush key "item 1" "item 2" "item 3" "item 4" "item 5" \
+                        "item 6" "item 7" "item 8" "item 9" "item 10"
+        }
+        assert {[string length [r dump key]] > (1024*64)}
+        start_server {tags {"repl"}} {
+            set second [srv 0 client]
+            set second_host [srv 0 host]
+            set second_port [srv 0 port]
+
+            assert {[$first exists key] == 1}
+            assert {[$second exists key] == 0}
+            set ret [r -1 migrate $second_host $second_port key 9 10000]
+            assert {$ret eq {OK}}
+            assert {[$first exists key] == 0}
+            assert {[$second exists key] == 1}
+            assert {[$second ttl key] == -1}
+            assert {[$second llen key] == 5000*20}
+        }
+    }
+
+    test {MIGRATE timeout actually works} {
+        set first [srv 0 client]
+        r set key "Some Value"
+        start_server {tags {"repl"}} {
+            set second [srv 0 client]
+            set second_host [srv 0 host]
+            set second_port [srv 0 port]
+
+            assert {[$first exists key] == 1}
+            assert {[$second exists key] == 0}
+
+            set rd [redis_deferring_client]
+            $rd debug sleep 5.0 ; # Make second server unable to reply.
+            set e {}
+            catch {r -1 migrate $second_host $second_port key 9 1000} e
+            assert_match {IOERR*} $e
+        }
+    }
+}
index a768e2ab98b8c821b0d11db7437073e5d07ee11c..9db0395a2bcd4206a540982d4053862f28eff917 100644 (file)
@@ -1,5 +1,22 @@
 start_server {tags {"introspection"}} {
     test {CLIENT LIST} {
         r client list
-    } {*addr=*:* fd=* age=* idle=* flags=N db=9 sub=0 psub=0 qbuf=0 qbuf-free=* obl=0 oll=0 omem=0 events=r cmd=client*}
+    } {*addr=*:* fd=* age=* idle=* flags=N db=9 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=* obl=0 oll=0 omem=0 events=r cmd=client*}
+
+    test {MONITOR can log executed commands} {
+        set rd [redis_deferring_client]
+        $rd monitor
+        r set foo bar
+        r get foo
+        list [$rd read] [$rd read] [$rd read]
+    } {*OK*"set" "foo"*"get" "foo"*}
+
+    test {MONITOR can log commands issued by the scripting engine} {
+        set rd [redis_deferring_client]
+        $rd monitor
+        r eval {redis.call('set',KEYS[1],ARGV[1])} 1 foo bar
+        $rd read ;# Discard the OK
+        assert_match {*eval*} [$rd read]
+        assert_match {*lua*"set"*"foo"*"bar"*} [$rd read]
+    }
 }
diff --git a/tests/unit/limits.tcl b/tests/unit/limits.tcl
new file mode 100644 (file)
index 0000000..b37ea9b
--- /dev/null
@@ -0,0 +1,16 @@
+start_server {tags {"limits"} overrides {maxclients 10}} {
+    test {Check if maxclients works refusing connections} {
+        set c 0
+        catch {
+            while {$c < 50} {
+                incr c
+                set rd [redis_deferring_client]
+                $rd ping
+                $rd read
+                after 100
+            }
+        } e
+        assert {$c > 8 && $c <= 10}
+        set e
+    } {*ERR max*reached*}
+}
index b9307cc1930fee9c9319a8e3ae54d1d5c911bd76..a60c65b437debfcaee9ca11b90a82ee060ddc070 100644 (file)
@@ -219,6 +219,96 @@ start_server {tags {"scripting"}} {
         list [r eval {return redis.sha1hex('')} 0] \
              [r eval {return redis.sha1hex('Pizza & Mandolino')} 0]
     } {da39a3ee5e6b4b0d3255bfef95601890afd80709 74822d82031af7493c20eefa13bd07ec4fada82f}
+
+    test {Globals protection reading an undeclared global variable} {
+        catch {r eval {return a} 0} e
+        set e
+    } {*ERR*attempted to access unexisting global*}
+
+    test {Globals protection setting an undeclared global*} {
+        catch {r eval {a=10} 0} e
+        set e
+    } {*ERR*attempted to create global*}
+
+    test {Test an example script DECR_IF_GT} {
+        set decr_if_gt {
+            local current
+
+            current = redis.call('get',KEYS[1])
+            if not current then return nil end
+            if current > ARGV[1] then
+                return redis.call('decr',KEYS[1])
+            else
+                return redis.call('get',KEYS[1])
+            end
+        }
+        r set foo 5
+        set res {}
+        lappend res [r eval $decr_if_gt 1 foo 2]
+        lappend res [r eval $decr_if_gt 1 foo 2]
+        lappend res [r eval $decr_if_gt 1 foo 2]
+        lappend res [r eval $decr_if_gt 1 foo 2]
+        lappend res [r eval $decr_if_gt 1 foo 2]
+        set res
+    } {4 3 2 2 2}
+
+    test {Scripting engine resets PRNG at every script execution} {
+        set rand1 [r eval {return tostring(math.random())} 0]
+        set rand2 [r eval {return tostring(math.random())} 0]
+        assert_equal $rand1 $rand2
+    }
+
+    test {Scripting engine PRNG can be seeded correctly} {
+        set rand1 [r eval {
+            math.randomseed(ARGV[1]); return tostring(math.random())
+        } 0 10]
+        set rand2 [r eval {
+            math.randomseed(ARGV[1]); return tostring(math.random())
+        } 0 10]
+        set rand3 [r eval {
+            math.randomseed(ARGV[1]); return tostring(math.random())
+        } 0 20]
+        assert_equal $rand1 $rand2
+        assert {$rand2 ne $rand3}
+    }
+}
+
+# Start a new server since the last test in this stanza will kill the
+# instance at all.
+start_server {tags {"scripting"}} {
+    test {Timedout read-only scripts can be killed by SCRIPT KILL} {
+        set rd [redis_deferring_client]
+        r config set lua-time-limit 10
+        $rd eval {while true do end} 0
+        after 200
+        catch {r ping} e
+        assert_match {BUSY*} $e
+        r script kill
+        assert_equal [r ping] "PONG"
+    }
+
+    test {Timedout scripts that modified data can't be killed by SCRIPT KILL} {
+        set rd [redis_deferring_client]
+        r config set lua-time-limit 10
+        $rd eval {redis.call('set','x','y'); while true do end} 0
+        after 200
+        catch {r ping} e
+        assert_match {BUSY*} $e
+        catch {r script kill} e
+        assert_match {ERR*} $e
+        catch {r ping} e
+        assert_match {BUSY*} $e
+    }
+
+    test {SHUTDOWN NOSAVE can kill a timedout script anyway} {
+        # The server sould be still unresponding to normal commands.
+        catch {r ping} e
+        assert_match {BUSY*} $e
+        catch {r shutdown nosave}
+        # Make sure the server was killed
+        catch {set rd [redis_deferring_client]} e
+        assert_match {*connection refused*} $e
+    }
 }
 
 start_server {tags {"scripting repl"}} {
index ba4122540e490cecffaaf34e847b5fff96acf496..5a181641cff8a935a947e24613d726276faef666 100644 (file)
@@ -190,6 +190,13 @@ start_server {
         r sort myset by score:*
     } {a aa aaa azz b c d e f g h i l m n o p q r s t u v z}
 
+    test "SORT GET with pattern ending with just -> does not get hash field" {
+        r del mylist
+        r lpush mylist a
+        r set x:a-> 100
+        r sort mylist by num get x:*->
+    } {100}
+
     tags {"slow"} {
         set num 100
         set res [create_random_dataset $num lpush]
index 47e10caab0de0662aa20d66066c2bc76ccabb813..950805d1bdd3b7d23cb0a685bd21bfec3809bca5 100644 (file)
@@ -395,4 +395,28 @@ start_server {tags {"hash"}} {
         r hset hash kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk b
         r hget hash kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
     } {b}
+
+    foreach size {10 512} {
+        test "Hash fuzzing - $size fields" {
+            for {set times 0} {$times < 10} {incr times} {
+                catch {unset hash}
+                array set hash {}
+                r del hash
+
+                # Create
+                for {set j 0} {$j < $size} {incr j} {
+                    set field [randomValue]
+                    set value [randomValue]
+                    r hset hash $field $value
+                    set hash($field) $value
+                }
+
+                # Verify
+                foreach {k v} [array get hash] {
+                    assert_equal $v [r hget hash $k]
+                }
+                assert_equal [array size hash] [r hlen hash]
+            }
+        }
+    }
 }
index 970e3ee7fb8fcf65719f8f6129a616aaa7f1e5ba..85dde5690a490093ccd3369418f6d6637e596bbc 100644 (file)
@@ -7,7 +7,7 @@ start_server {
 } {
     source "tests/unit/type/list-common.tcl"
 
-    test {LPUSH, RPUSH, LLENGTH, LINDEX - ziplist} {
+    test {LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - ziplist} {
         # first lpush then rpush
         assert_equal 1 [r lpush myziplist1 a]
         assert_equal 2 [r rpush myziplist1 b]
@@ -16,6 +16,9 @@ start_server {
         assert_equal a [r lindex myziplist1 0]
         assert_equal b [r lindex myziplist1 1]
         assert_equal c [r lindex myziplist1 2]
+        assert_equal {} [r lindex myziplist2 3]
+        assert_equal c [r rpop myziplist1]
+        assert_equal a [r lpop myziplist1]
         assert_encoding ziplist myziplist1
 
         # first rpush then lpush
@@ -26,10 +29,13 @@ start_server {
         assert_equal c [r lindex myziplist2 0]
         assert_equal b [r lindex myziplist2 1]
         assert_equal a [r lindex myziplist2 2]
+        assert_equal {} [r lindex myziplist2 3]
+        assert_equal a [r rpop myziplist2]
+        assert_equal c [r lpop myziplist2]
         assert_encoding ziplist myziplist2
     }
 
-    test {LPUSH, RPUSH, LLENGTH, LINDEX - regular list} {
+    test {LPUSH, RPUSH, LLENGTH, LINDEX, LPOP - regular list} {
         # first lpush then rpush
         assert_equal 1 [r lpush mylist1 $largevalue(linkedlist)]
         assert_encoding linkedlist mylist1
@@ -39,6 +45,9 @@ start_server {
         assert_equal $largevalue(linkedlist) [r lindex mylist1 0]
         assert_equal b [r lindex mylist1 1]
         assert_equal c [r lindex mylist1 2]
+        assert_equal {} [r lindex mylist1 3]
+        assert_equal c [r rpop mylist1]
+        assert_equal $largevalue(linkedlist) [r lpop mylist1]
 
         # first rpush then lpush
         assert_equal 1 [r rpush mylist2 $largevalue(linkedlist)]
@@ -49,8 +58,15 @@ start_server {
         assert_equal c [r lindex mylist2 0]
         assert_equal b [r lindex mylist2 1]
         assert_equal $largevalue(linkedlist) [r lindex mylist2 2]
+        assert_equal {} [r lindex mylist2 3]
+        assert_equal $largevalue(linkedlist) [r rpop mylist2]
+        assert_equal c [r lpop mylist2]
     }
 
+    test {R/LPOP against empty list} {
+        r lpop non-existing-list
+    } {}
+
     test {Variadic RPUSH/LPUSH} {
         r del mylist
         assert_equal 4 [r lpush mylist a b c d]
@@ -396,6 +412,11 @@ start_server {
         }
     }
 
+    test {LINSERT raise error on bad syntax} {
+        catch {[r linsert xlist aft3r aa 42]} e
+        set e
+    } {*ERR*syntax*error*}
+
     test {LPUSHX, RPUSHX convert from ziplist to list} {
         set large $largevalue(linkedlist)
 
index bdd1f9bfa11955a2026ccb9caad974204126267a..f4f2837351fc9da650363d83136e15be89fb1668 100644 (file)
@@ -206,6 +206,13 @@ start_server {
         }
     }
 
+    test "SDIFF with first set empty" {
+        r del set1 set2 set3
+        r sadd set2 1 2 3 4
+        r sadd set3 a b c d
+        r sdiff set1 set2 set3
+    } {}
+
     test "SINTER against non-set should throw error" {
         r set key1 x
         assert_error "ERR*wrong kind*" {r sinter key1 noset}
@@ -216,6 +223,23 @@ start_server {
         assert_error "ERR*wrong kind*" {r sunion key1 noset}
     }
 
+    test "SINTER should handle non existing key as empty" {
+        r del set1 set2 set3
+        r sadd set1 a b c
+        r sadd set2 b c d
+        r sinter set1 set2 set3
+    } {}
+
+    test "SINTER with same integer elements but different encoding" {
+        r del set1 set2
+        r sadd set1 1 2 3
+        r sadd set2 1 2 3 a
+        r srem set2 a
+        assert_encoding intset set1
+        assert_encoding hashtable set2
+        lsort [r sinter set1 set2]
+    } {1 2 3}
+
     test "SINTERSTORE against non existing keys should delete dstkey" {
         r set setres xxx
         assert_equal 0 [r sinterstore setres foo111 bar222]
@@ -317,6 +341,13 @@ start_server {
         assert_error "ERR*wrong kind*" {r smove myset2 x foo}
     }
 
+    test "SMOVE with identical source and destination" {
+        r del set
+        r sadd set a b c
+        r smove set set b
+        lsort [r smembers set]
+    } {a b c}
+
     tags {slow} {
         test {intsets implementation stress testing} {
             for {set j 0} {$j < 20} {incr j} {
index 96cccc2bfbc1a8e0be5cb4379c48c434068a1fde..f6ca8874b4ebb937d861f39aa5e6426465829569 100755 (executable)
@@ -10,7 +10,8 @@ GROUPS = [
   "pubsub",
   "transactions",
   "connection",
-  "server"
+  "server",
+  "scripting"
 ].freeze
 
 GROUPS_BY_NAME = Hash[*
@@ -48,7 +49,7 @@ def commands
   require "json"
   require "uri"
 
-  url = URI.parse "https://github.com/antirez/redis-doc/raw/master/commands.json"
+  url = URI.parse "https://raw.github.com/antirez/redis-doc/master/commands.json"
   client = Net::HTTP.new url.host, url.port
   client.use_ssl = true
   response = client.get url.path
index 06570a87f7c2f6ad0e6b9ea29f36b3d071f03ae9..93b5b411b803da80c4f91a94b4e28cb981e2ab05 100755 (executable)
@@ -166,7 +166,7 @@ if [[ ! `which chkconfig` ]] ; then
 else
        # we're chkconfig, so lets add to chkconfig and put in runlevel 345
        chkconfig --add redis_$REDIS_PORT && echo "Successfully added to chkconfig!"
-       chkconfig--level 345 redis_$REDIS_PORT on && echo "Successfully added to runlevels 345!"
+       chkconfig --level 345 redis_$REDIS_PORT on && echo "Successfully added to runlevels 345!"
 fi
        
 /etc/init.d/redis_$REDIS_PORT start || die "Failed starting service..."