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
# 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"
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
-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
# 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
############################## 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
#
# 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
# 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
--- /dev/null
+*.gcda
+*.gcno
+*.gcov
+redis.info
+lcov-html
# 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
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
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)
--- /dev/null
+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
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
+#include <poll.h>
#include <string.h>
#include "ae.h"
/* 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;
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;
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 */
#include "redis.h"
+#include "endianconv.h"
#include <arpa/inet.h>
#include <fcntl.h>
}
/* -----------------------------------------------------------------------------
- * 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;
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))
/* 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++;
int fd;
long timeout;
long dbid;
- time_t ttl;
+ long long ttl = 0, expireat;
robj *o;
rio cmd, payload;
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. */
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;
}
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
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) {
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;
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);
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. */
#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) || \
--- /dev/null
+/* 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
* 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) {
*
* 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));
addReply(c, shared.cone);
return;
} else {
- long long when = mstime()+milliseconds;
setExpire(c->db,key,when);
addReply(c,shared.cone);
signalModifiedKey(c->db,key);
}
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) {
mixDigest(digest,key,sdslen(key));
- /* Make sure the key is loaded if VM is active */
o = dictGetVal(de);
aux = htonl(o->type);
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;
}
}
-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)
-/* 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
"pubsub",
"transactions",
"connection",
- "server"
+ "server",
+ "scripting"
};
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
#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;
}
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);
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),
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,
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;
}
#include "redis.h"
#include "lzf.h" /* LZF compression library */
#include "zipmap.h"
+#include "endianconv.h"
#include <math.h>
#include <sys/types.h>
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;
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");
}
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;
}
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));
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;
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;
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) {
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;
/* 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);
}
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;
/* 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:
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 */
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")) {
" --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"
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;
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;
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.
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
}
#include <float.h>
#include <math.h>
#include <sys/resource.h>
+#include <sys/utsname.h>
/* Our shared "common" objects */
/*================================= Globals ================================= */
+/* Alternate stack for SIGSEGV/etc handlers */
+char altstack[SIGSTKSZ];
+
/* Global vars */
struct redisServer server; /* server global state */
struct redisCommand *commandTable;
{"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},
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);
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);
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);
}
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
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);
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;
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;
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));
/* 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 {
/* 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 &&
/* 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"
REDIS_VERSION,
redisGitSHA1(),
strtol(redisGitDirty(),NULL,10) > 0,
+ name.sysname, name.release, name.machine,
server.arch_bits,
aeGetApiName(),
#ifdef __GNUC__
"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,
}
}
- /* Clusetr */
+ /* Cluster */
if (allsections || defsections || !strcasecmp(section,"cluster")) {
if (sections++) info = sdscat(info,"\r\n");
info = sdscatprintf(info,
}
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);
}
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;
#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 */
#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
*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" */
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 */
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);
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);
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);
/* 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));
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;
}
/* 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;
+/* 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);
rioBufferRead,
rioBufferWrite,
rioBufferTell,
+ NULL, /* update_checksum */
+ 0, /* current checksum */
{ { NULL, 0 } } /* union for io-specific vars */
};
rioFileRead,
rioFileWrite,
rioFileTell,
+ NULL, /* update_checksum */
+ 0, /* current checksum */
{ { NULL, 0 } } /* union for io-specific vars */
};
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];
#define __REDIS_RIO_H
#include <stdio.h>
+#include <stdint.h>
#include "sds.h"
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 {
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);
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
#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.
" 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);
}
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;
}
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);
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. */
* 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);
}
/* 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
* 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--;
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]);
-#define REDIS_VERSION "2.9.5"
+#define REDIS_VERSION "2.9.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
#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
--- /dev/null
+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]
--- /dev/null
+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]
+ }
+ }
+}
[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]} {
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
}
}
# 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
}
integration/replication
integration/replication-2
integration/replication-3
+ integration/replication-4
integration/aof
integration/rdb
integration/convert-zipmap-hash-on-load
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
"--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"]
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
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" {
}
}
}
+
+ 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
+ }
+ }
}
--- /dev/null
+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
+ }
+ }
+}
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]
+ }
}
--- /dev/null
+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*}
+}
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"}} {
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]
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]
+ }
+ }
+ }
}
} {
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]
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
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
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)]
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]
}
}
+ 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)
}
}
+ 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}
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]
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} {
"pubsub",
"transactions",
"connection",
- "server"
+ "server",
+ "scripting"
].freeze
GROUPS_BY_NAME = Hash[*
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
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..."