Unverified Commit 49816941 authored by chendianqiang's avatar chendianqiang Committed by GitHub
Browse files

Merge pull request #2 from antirez/unstable

merge from redis
parents 68ceb466 f311a529
...@@ -119,7 +119,7 @@ parameter (the path of the configuration file): ...@@ -119,7 +119,7 @@ parameter (the path of the configuration file):
It is possible to alter the Redis configuration by passing parameters directly It is possible to alter the Redis configuration by passing parameters directly
as options using the command line. Examples: as options using the command line. Examples:
% ./redis-server --port 9999 --slaveof 127.0.0.1 6379 % ./redis-server --port 9999 --replicaof 127.0.0.1 6379
% ./redis-server /etc/redis/6379.conf --loglevel debug % ./redis-server /etc/redis/6379.conf --loglevel debug
All the options in redis.conf are also supported as options using the command All the options in redis.conf are also supported as options using the command
...@@ -216,7 +216,7 @@ Inside the root are the following important directories: ...@@ -216,7 +216,7 @@ Inside the root are the following important directories:
* `src`: contains the Redis implementation, written in C. * `src`: contains the Redis implementation, written in C.
* `tests`: contains the unit tests, implemented in Tcl. * `tests`: contains the unit tests, implemented in Tcl.
* `deps`: contains libraries Redis uses. Everything needed to compile Redis is inside this directory; your system just needs to provide `libc`, a POSIX compatible interface and a C compiler. Notably `deps` contains a copy of `jemalloc`, which is the default allocator of Redis under Linux. Note that under `deps` there are also things which started with the Redis project, but for which the main repository is not `anitrez/redis`. An exception to this rule is `deps/geohash-int` which is the low level geocoding library used by Redis: it originated from a different project, but at this point it diverged so much that it is developed as a separated entity directly inside the Redis repository. * `deps`: contains libraries Redis uses. Everything needed to compile Redis is inside this directory; your system just needs to provide `libc`, a POSIX compatible interface and a C compiler. Notably `deps` contains a copy of `jemalloc`, which is the default allocator of Redis under Linux. Note that under `deps` there are also things which started with the Redis project, but for which the main repository is not `antirez/redis`.
There are a few more directories but they are not very important for our goals There are a few more directories but they are not very important for our goals
here. We'll focus mostly on `src`, where the Redis implementation is contained, here. We'll focus mostly on `src`, where the Redis implementation is contained,
...@@ -227,7 +227,7 @@ of complexity incrementally. ...@@ -227,7 +227,7 @@ of complexity incrementally.
Note: lately Redis was refactored quite a bit. Function names and file Note: lately Redis was refactored quite a bit. Function names and file
names have been changed, so you may find that this documentation reflects the names have been changed, so you may find that this documentation reflects the
`unstable` branch more closely. For instance in Redis 3.0 the `server.c` `unstable` branch more closely. For instance in Redis 3.0 the `server.c`
and `server.h` files were named to `redis.c` and `redis.h`. However the overall and `server.h` files were named `redis.c` and `redis.h`. However the overall
structure is the same. Keep in mind that all the new developments and pull structure is the same. Keep in mind that all the new developments and pull
requests should be performed against the `unstable` branch. requests should be performed against the `unstable` branch.
...@@ -245,7 +245,7 @@ A few important fields in this structure are: ...@@ -245,7 +245,7 @@ A few important fields in this structure are:
* `server.db` is an array of Redis databases, where data is stored. * `server.db` is an array of Redis databases, where data is stored.
* `server.commands` is the command table. * `server.commands` is the command table.
* `server.clients` is a linked list of clients connected to the server. * `server.clients` is a linked list of clients connected to the server.
* `server.master` is a special client, the master, if the instance is a slave. * `server.master` is a special client, the master, if the instance is a replica.
There are tons of other fields. Most fields are commented directly inside There are tons of other fields. Most fields are commented directly inside
the structure definition. the structure definition.
...@@ -323,7 +323,7 @@ Inside server.c you can find code that handles other vital things of the Redis s ...@@ -323,7 +323,7 @@ Inside server.c you can find code that handles other vital things of the Redis s
networking.c networking.c
--- ---
This file defines all the I/O functions with clients, masters and slaves This file defines all the I/O functions with clients, masters and replicas
(which in Redis are just special clients): (which in Redis are just special clients):
* `createClient()` allocates and initializes a new client. * `createClient()` allocates and initializes a new client.
...@@ -390,16 +390,16 @@ replication.c ...@@ -390,16 +390,16 @@ replication.c
This is one of the most complex files inside Redis, it is recommended to This is one of the most complex files inside Redis, it is recommended to
approach it only after getting a bit familiar with the rest of the code base. approach it only after getting a bit familiar with the rest of the code base.
In this file there is the implementation of both the master and slave role In this file there is the implementation of both the master and replica role
of Redis. of Redis.
One of the most important functions inside this file is `replicationFeedSlaves()` that writes commands to the clients representing slave instances connected One of the most important functions inside this file is `replicationFeedSlaves()` that writes commands to the clients representing replica instances connected
to our master, so that the slaves can get the writes performed by the clients: to our master, so that the replicas can get the writes performed by the clients:
this way their data set will remain synchronized with the one in the master. this way their data set will remain synchronized with the one in the master.
This file also implements both the `SYNC` and `PSYNC` commands that are This file also implements both the `SYNC` and `PSYNC` commands that are
used in order to perform the first synchronization between masters and used in order to perform the first synchronization between masters and
slaves, or to continue the replication after a disconnection. replicas, or to continue the replication after a disconnection.
Other C files Other C files
--- ---
......
...@@ -2,7 +2,6 @@ This directory contains all Redis dependencies, except for the libc that ...@@ -2,7 +2,6 @@ This directory contains all Redis dependencies, except for the libc that
should be provided by the operating system. should be provided by the operating system.
* **Jemalloc** is our memory allocator, used as replacement for libc malloc on Linux by default. It has good performances and excellent fragmentation behavior. This component is upgraded from time to time. * **Jemalloc** is our memory allocator, used as replacement for libc malloc on Linux by default. It has good performances and excellent fragmentation behavior. This component is upgraded from time to time.
* **geohash-int** is inside the dependencies directory but is actually part of the Redis project, since it is our private fork (heavily modified) of a library initially developed for Ardb, which is in turn a fork of Redis.
* **hiredis** is the official C client library for Redis. It is used by redis-cli, redis-benchmark and Redis Sentinel. It is part of the Redis official ecosystem but is developed externally from the Redis repository, so we just upgrade it as needed. * **hiredis** is the official C client library for Redis. It is used by redis-cli, redis-benchmark and Redis Sentinel. It is part of the Redis official ecosystem but is developed externally from the Redis repository, so we just upgrade it as needed.
* **linenoise** is a readline replacement. It is developed by the same authors of Redis but is managed as a separated project and updated as needed. * **linenoise** is a readline replacement. It is developed by the same authors of Redis but is managed as a separated project and updated as needed.
* **lua** is Lua 5.1 with minor changes for security and additional libraries. * **lua** is Lua 5.1 with minor changes for security and additional libraries.
...@@ -42,11 +41,6 @@ the following additional steps: ...@@ -42,11 +41,6 @@ the following additional steps:
changed, otherwise you could just copy the old implementation if you are changed, otherwise you could just copy the old implementation if you are
upgrading just to a similar version of Jemalloc. upgrading just to a similar version of Jemalloc.
Geohash
---
This is never upgraded since it's part of the Redis project. If there are changes to merge from Ardb there is the need to manually check differences, but at this point the source code is pretty different.
Hiredis Hiredis
--- ---
......
...@@ -8,6 +8,12 @@ os: ...@@ -8,6 +8,12 @@ os:
- linux - linux
- osx - osx
branches:
only:
- staging
- trying
- master
before_script: before_script:
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew update; brew install redis; fi - if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew update; brew install redis; fi
......
### 1.0.0 (unreleased) ### 1.0.0 (unreleased)
**Fixes**: **BREAKING CHANGES**:
* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now
protocol errors. This is consistent with the RESP specification. On 32-bit
platforms, the upper bound is lowered to `SIZE_MAX`.
* Change `redisReply.len` to `size_t`, as it denotes the the size of a string
User code should compare this to `size_t` values as well. If it was used to
compare to other values, casting might be necessary or can be removed, if
casting was applied before.
### 0.14.0 (2018-09-25)
* Make string2ll static to fix conflict with Redis (Tom Lee [c3188b])
* Use -dynamiclib instead of -shared for OSX (Ryan Schmidt [a65537])
* Use string2ll from Redis w/added tests (Michael Grunder [7bef04, 60f622])
* Makefile - OSX compilation fixes (Ryan Schmidt [881fcb, 0e9af8])
* Remove redundant NULL checks (Justin Brewer [54acc8, 58e6b8])
* Fix bulk and multi-bulk length truncation (Justin Brewer [109197])
* Fix SIGSEGV in OpenBSD by checking for NULL before calling freeaddrinfo (Justin Brewer [546d94])
* Several POSIX compatibility fixes (Justin Brewer [bbeab8, 49bbaa, d1c1b6])
* Makefile - Compatibility fixes (Dimitri Vorobiev [3238cf, 12a9d1])
* Makefile - Fix make install on FreeBSD (Zach Shipko [a2ef2b])
* Makefile - don't assume $(INSTALL) is cp (Igor Gnatenko [725a96])
* Separate side-effect causing function from assert and small cleanup (amallia [b46413, 3c3234])
* Don't send negative values to `__redisAsyncCommand` (Frederik Deweerdt [706129])
* Fix leak if setsockopt fails (Frederik Deweerdt [e21c9c])
* Fix libevent leak (zfz [515228])
* Clean up GCC warning (Ichito Nagata [2ec774])
* Keep track of errno in `__redisSetErrorFromErrno()` as snprintf may use it (Jin Qing [25cd88])
* Solaris compilation fix (Donald Whyte [41b07d])
* Reorder linker arguments when building examples (Tustfarm-heart [06eedd])
* Keep track of subscriptions in case of rapid subscribe/unsubscribe (Hyungjin Kim [073dc8, be76c5, d46999])
* libuv use after free fix (Paul Scott [cbb956])
* Properly close socket fd on reconnect attempt (WSL [64d1ec])
* Skip valgrind in OSX tests (Jan-Erik Rediger [9deb78])
* Various updates for Travis testing OSX (Ted Nyman [fa3774, 16a459, bc0ea5])
* Update libevent (Chris Xin [386802])
* Change sds.h for building in C++ projects (Ali Volkan ATLI [f5b32e])
* Use proper format specifier in redisFormatSdsCommandArgv (Paulino Huerta, Jan-Erik Rediger [360a06, 8655a6])
* Better handling of NULL reply in example code (Jan-Erik Rediger [1b8ed3])
* Prevent overflow when formatting an error (Jan-Erik Rediger [0335cb])
* Compatibility fix for strerror_r (Tom Lee [bb1747])
* Properly detect integer parse/overflow errors (Justin Brewer [93421f])
* Adds CI for Windows and cygwin fixes (owent, [6c53d6, 6c3e40])
* Catch a buffer overflow when formatting the error message * Catch a buffer overflow when formatting the error message
* Import latest upstream sds. This breaks applications that are linked against the old hiredis v0.13 * Import latest upstream sds. This breaks applications that are linked against the old hiredis v0.13
* Fix warnings, when compiled with -Wshadow * Fix warnings, when compiled with -Wshadow
...@@ -9,11 +53,6 @@ ...@@ -9,11 +53,6 @@
**BREAKING CHANGES**: **BREAKING CHANGES**:
* Change `redisReply.len` to `size_t`, as it denotes the the size of a string
User code should compare this to `size_t` values as well.
If it was used to compare to other values, casting might be necessary or can be removed, if casting was applied before.
* Remove backwards compatibility macro's * Remove backwards compatibility macro's
This removes the following old function aliases, use the new name now: This removes the following old function aliases, use the new name now:
...@@ -94,7 +133,7 @@ The parser, standalone since v0.12.0, can now be compiled on Windows ...@@ -94,7 +133,7 @@ The parser, standalone since v0.12.0, can now be compiled on Windows
* Add IPv6 support * Add IPv6 support
* Remove possiblity of multiple close on same fd * Remove possibility of multiple close on same fd
* Add ability to bind source address on connect * Add ability to bind source address on connect
......
...@@ -36,13 +36,13 @@ endef ...@@ -36,13 +36,13 @@ endef
export REDIS_TEST_CONFIG export REDIS_TEST_CONFIG
# Fallback to gcc when $CC is not in $PATH. # Fallback to gcc when $CC is not in $PATH.
CC:=$(shell sh -c 'type $(CC) >/dev/null 2>/dev/null && echo $(CC) || echo gcc') CC:=$(shell sh -c 'type $${CC%% *} >/dev/null 2>/dev/null && echo $(CC) || echo gcc')
CXX:=$(shell sh -c 'type $(CXX) >/dev/null 2>/dev/null && echo $(CXX) || echo g++') CXX:=$(shell sh -c 'type $${CXX%% *} >/dev/null 2>/dev/null && echo $(CXX) || echo g++')
OPTIMIZATION?=-O3 OPTIMIZATION?=-O3
WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings
DEBUG_FLAGS?= -g -ggdb DEBUG_FLAGS?= -g -ggdb
REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS) $(ARCH) REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CPPFLAGS) $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS)
REAL_LDFLAGS=$(LDFLAGS) $(ARCH) REAL_LDFLAGS=$(LDFLAGS)
DYLIBSUFFIX=so DYLIBSUFFIX=so
STLIBSUFFIX=a STLIBSUFFIX=a
...@@ -58,12 +58,11 @@ uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') ...@@ -58,12 +58,11 @@ uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
ifeq ($(uname_S),SunOS) ifeq ($(uname_S),SunOS)
REAL_LDFLAGS+= -ldl -lnsl -lsocket REAL_LDFLAGS+= -ldl -lnsl -lsocket
DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS) DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)
INSTALL= cp -r
endif endif
ifeq ($(uname_S),Darwin) ifeq ($(uname_S),Darwin)
DYLIBSUFFIX=dylib DYLIBSUFFIX=dylib
DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_SONAME).$(DYLIBSUFFIX) DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_SONAME).$(DYLIBSUFFIX)
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-install_name,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS) DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
endif endif
all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME) all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME)
...@@ -94,7 +93,7 @@ hiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME) ...@@ -94,7 +93,7 @@ hiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME)
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -lev $(STLIBNAME) $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -lev $(STLIBNAME)
hiredis-example-glib: examples/example-glib.c adapters/glib.h $(STLIBNAME) hiredis-example-glib: examples/example-glib.c adapters/glib.h $(STLIBNAME)
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) $(shell pkg-config --cflags --libs glib-2.0) -I. $< $(STLIBNAME) $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< $(shell pkg-config --cflags --libs glib-2.0) $(STLIBNAME)
hiredis-example-ivykis: examples/example-ivykis.c adapters/ivykis.h $(STLIBNAME) hiredis-example-ivykis: examples/example-ivykis.c adapters/ivykis.h $(STLIBNAME)
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -livykis $(STLIBNAME) $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -livykis $(STLIBNAME)
...@@ -161,11 +160,7 @@ clean: ...@@ -161,11 +160,7 @@ clean:
dep: dep:
$(CC) -MM *.c $(CC) -MM *.c
ifeq ($(uname_S),SunOS) INSTALL?= cp -pPR
INSTALL?= cp -r
endif
INSTALL?= cp -a
$(PKGCONFNAME): hiredis.h $(PKGCONFNAME): hiredis.h
@echo "Generating $@ for pkgconfig..." @echo "Generating $@ for pkgconfig..."
...@@ -181,8 +176,9 @@ $(PKGCONFNAME): hiredis.h ...@@ -181,8 +176,9 @@ $(PKGCONFNAME): hiredis.h
@echo Cflags: -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@ @echo Cflags: -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@
install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME) install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME)
mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH) mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH)
$(INSTALL) hiredis.h async.h read.h sds.h adapters $(INSTALL_INCLUDE_PATH) $(INSTALL) hiredis.h async.h read.h sds.h $(INSTALL_INCLUDE_PATH)
$(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters
$(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME) $(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME)
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME) cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME)
$(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH) $(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)
......
...@@ -73,8 +73,8 @@ static void redisLibeventDelWrite(void *privdata) { ...@@ -73,8 +73,8 @@ static void redisLibeventDelWrite(void *privdata) {
static void redisLibeventCleanup(void *privdata) { static void redisLibeventCleanup(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata; redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(e->rev); event_free(e->rev);
event_del(e->wev); event_free(e->wev);
free(e); free(e);
} }
......
...@@ -15,15 +15,12 @@ typedef struct redisLibuvEvents { ...@@ -15,15 +15,12 @@ typedef struct redisLibuvEvents {
static void redisLibuvPoll(uv_poll_t* handle, int status, int events) { static void redisLibuvPoll(uv_poll_t* handle, int status, int events) {
redisLibuvEvents* p = (redisLibuvEvents*)handle->data; redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
int ev = (status ? p->events : events);
if (status != 0) { if (p->context != NULL && (ev & UV_READABLE)) {
return;
}
if (p->context != NULL && (events & UV_READABLE)) {
redisAsyncHandleRead(p->context); redisAsyncHandleRead(p->context);
} }
if (p->context != NULL && (events & UV_WRITABLE)) { if (p->context != NULL && (ev & UV_WRITABLE)) {
redisAsyncHandleWrite(p->context); redisAsyncHandleWrite(p->context);
} }
} }
......
# Appveyor configuration file for CI build of hiredis on Windows (under Cygwin) # Appveyor configuration file for CI build of hiredis on Windows (under Cygwin)
environment: environment:
matrix: matrix:
- CYG_ROOT: C:\cygwin64 - CYG_BASH: C:\cygwin64\bin\bash
CYG_SETUP: setup-x86_64.exe
CYG_MIRROR: http://cygwin.mirror.constant.com
CYG_CACHE: C:\cygwin64\var\cache\setup
CYG_BASH: C:\cygwin64\bin\bash
CC: gcc CC: gcc
- CYG_ROOT: C:\cygwin - CYG_BASH: C:\cygwin\bin\bash
CYG_SETUP: setup-x86.exe
CYG_MIRROR: http://cygwin.mirror.constant.com
CYG_CACHE: C:\cygwin\var\cache\setup
CYG_BASH: C:\cygwin\bin\bash
CC: gcc CC: gcc
TARGET: 32bit TARGET: 32bit
TARGET_VARS: 32bit-vars TARGET_VARS: 32bit-vars
# Cache Cygwin files to speed up build
cache:
- '%CYG_CACHE%'
clone_depth: 1 clone_depth: 1
# Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail # Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail
...@@ -27,8 +16,6 @@ init: ...@@ -27,8 +16,6 @@ init:
# Install needed build dependencies # Install needed build dependencies
install: install:
- ps: 'Start-FileDownload "http://cygwin.com/$env:CYG_SETUP" -FileName "$env:CYG_SETUP"'
- '%CYG_SETUP% --quiet-mode --no-shortcuts --only-site --root "%CYG_ROOT%" --site "%CYG_MIRROR%" --local-package-dir "%CYG_CACHE%" --packages automake,bison,gcc-core,libtool,make,gettext-devel,gettext,intltool,pkg-config,clang,llvm > NUL 2>&1'
- '%CYG_BASH% -lc "cygcheck -dc cygwin"' - '%CYG_BASH% -lc "cygcheck -dc cygwin"'
build_script: build_script:
......
...@@ -336,7 +336,8 @@ static void __redisAsyncDisconnect(redisAsyncContext *ac) { ...@@ -336,7 +336,8 @@ static void __redisAsyncDisconnect(redisAsyncContext *ac) {
if (ac->err == 0) { if (ac->err == 0) {
/* For clean disconnects, there should be no pending callbacks. */ /* For clean disconnects, there should be no pending callbacks. */
assert(__redisShiftCallback(&ac->replies,NULL) == REDIS_ERR); int ret = __redisShiftCallback(&ac->replies,NULL);
assert(ret == REDIS_ERR);
} else { } else {
/* Disconnection is caused by an error, make sure that pending /* Disconnection is caused by an error, make sure that pending
* callbacks cannot call new commands. */ * callbacks cannot call new commands. */
...@@ -364,6 +365,7 @@ void redisAsyncDisconnect(redisAsyncContext *ac) { ...@@ -364,6 +365,7 @@ void redisAsyncDisconnect(redisAsyncContext *ac) {
static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) { static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) {
redisContext *c = &(ac->c); redisContext *c = &(ac->c);
dict *callbacks; dict *callbacks;
redisCallback *cb;
dictEntry *de; dictEntry *de;
int pvariant; int pvariant;
char *stype; char *stype;
...@@ -387,16 +389,28 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, ...@@ -387,16 +389,28 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len); sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len);
de = dictFind(callbacks,sname); de = dictFind(callbacks,sname);
if (de != NULL) { if (de != NULL) {
memcpy(dstcb,dictGetEntryVal(de),sizeof(*dstcb)); cb = dictGetEntryVal(de);
/* If this is an subscribe reply decrease pending counter. */
if (strcasecmp(stype+pvariant,"subscribe") == 0) {
cb->pending_subs -= 1;
}
memcpy(dstcb,cb,sizeof(*dstcb));
/* If this is an unsubscribe message, remove it. */ /* If this is an unsubscribe message, remove it. */
if (strcasecmp(stype+pvariant,"unsubscribe") == 0) { if (strcasecmp(stype+pvariant,"unsubscribe") == 0) {
if (cb->pending_subs == 0)
dictDelete(callbacks,sname); dictDelete(callbacks,sname);
/* If this was the last unsubscribe message, revert to /* If this was the last unsubscribe message, revert to
* non-subscribe mode. */ * non-subscribe mode. */
assert(reply->element[2]->type == REDIS_REPLY_INTEGER); assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
if (reply->element[2]->integer == 0)
/* Unset subscribed flag only when no pipelined pending subscribe. */
if (reply->element[2]->integer == 0
&& dictSize(ac->sub.channels) == 0
&& dictSize(ac->sub.patterns) == 0)
c->flags &= ~REDIS_SUBSCRIBED; c->flags &= ~REDIS_SUBSCRIBED;
} }
} }
...@@ -410,7 +424,7 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, ...@@ -410,7 +424,7 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
void redisProcessCallbacks(redisAsyncContext *ac) { void redisProcessCallbacks(redisAsyncContext *ac) {
redisContext *c = &(ac->c); redisContext *c = &(ac->c);
redisCallback cb = {NULL, NULL, NULL}; redisCallback cb = {NULL, NULL, 0, NULL};
void *reply = NULL; void *reply = NULL;
int status; int status;
...@@ -492,22 +506,22 @@ void redisProcessCallbacks(redisAsyncContext *ac) { ...@@ -492,22 +506,22 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
* write event fires. When connecting was not successful, the connect callback * write event fires. When connecting was not successful, the connect callback
* is called with a REDIS_ERR status and the context is free'd. */ * is called with a REDIS_ERR status and the context is free'd. */
static int __redisAsyncHandleConnect(redisAsyncContext *ac) { static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
int completed = 0;
redisContext *c = &(ac->c); redisContext *c = &(ac->c);
if (redisCheckConnectDone(c, &completed) == REDIS_ERR) {
if (redisCheckSocketError(c) == REDIS_ERR) { /* Error! */
/* Try again later when connect(2) is still in progress. */ redisCheckSocketError(c);
if (errno == EINPROGRESS) if (ac->onConnect) ac->onConnect(ac, REDIS_ERR);
return REDIS_OK;
if (ac->onConnect) ac->onConnect(ac,REDIS_ERR);
__redisAsyncDisconnect(ac); __redisAsyncDisconnect(ac);
return REDIS_ERR; return REDIS_ERR;
} } else if (completed == 1) {
/* connected! */
/* Mark context as connected. */ if (ac->onConnect) ac->onConnect(ac, REDIS_OK);
c->flags |= REDIS_CONNECTED; c->flags |= REDIS_CONNECTED;
if (ac->onConnect) ac->onConnect(ac,REDIS_OK);
return REDIS_OK; return REDIS_OK;
} else {
return REDIS_OK;
}
} }
/* This function should be called when the socket is readable. /* This function should be called when the socket is readable.
...@@ -583,6 +597,9 @@ static const char *nextArgument(const char *start, const char **str, size_t *len ...@@ -583,6 +597,9 @@ static const char *nextArgument(const char *start, const char **str, size_t *len
static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) { static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {
redisContext *c = &(ac->c); redisContext *c = &(ac->c);
redisCallback cb; redisCallback cb;
struct dict *cbdict;
dictEntry *de;
redisCallback *existcb;
int pvariant, hasnext; int pvariant, hasnext;
const char *cstr, *astr; const char *cstr, *astr;
size_t clen, alen; size_t clen, alen;
...@@ -596,6 +613,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void ...@@ -596,6 +613,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
/* Setup callback */ /* Setup callback */
cb.fn = fn; cb.fn = fn;
cb.privdata = privdata; cb.privdata = privdata;
cb.pending_subs = 1;
/* Find out which command will be appended. */ /* Find out which command will be appended. */
p = nextArgument(cmd,&cstr,&clen); p = nextArgument(cmd,&cstr,&clen);
...@@ -612,9 +630,18 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void ...@@ -612,9 +630,18 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
while ((p = nextArgument(p,&astr,&alen)) != NULL) { while ((p = nextArgument(p,&astr,&alen)) != NULL) {
sname = sdsnewlen(astr,alen); sname = sdsnewlen(astr,alen);
if (pvariant) if (pvariant)
ret = dictReplace(ac->sub.patterns,sname,&cb); cbdict = ac->sub.patterns;
else else
ret = dictReplace(ac->sub.channels,sname,&cb); cbdict = ac->sub.channels;
de = dictFind(cbdict,sname);
if (de != NULL) {
existcb = dictGetEntryVal(de);
cb.pending_subs = existcb->pending_subs + 1;
}
ret = dictReplace(cbdict,sname,&cb);
if (ret == 0) sdsfree(sname); if (ret == 0) sdsfree(sname);
} }
...@@ -676,6 +703,8 @@ int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *priv ...@@ -676,6 +703,8 @@ int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *priv
int len; int len;
int status; int status;
len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen); len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
if (len < 0)
return REDIS_ERR;
status = __redisAsyncCommand(ac,fn,privdata,cmd,len); status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
sdsfree(cmd); sdsfree(cmd);
return status; return status;
......
...@@ -45,6 +45,7 @@ typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*); ...@@ -45,6 +45,7 @@ typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*);
typedef struct redisCallback { typedef struct redisCallback {
struct redisCallback *next; /* simple singly linked list */ struct redisCallback *next; /* simple singly linked list */
redisCallbackFn *fn; redisCallbackFn *fn;
int pending_subs;
void *privdata; void *privdata;
} redisCallback; } redisCallback;
...@@ -92,6 +93,10 @@ typedef struct redisAsyncContext { ...@@ -92,6 +93,10 @@ typedef struct redisAsyncContext {
/* Regular command callbacks */ /* Regular command callbacks */
redisCallbackList replies; redisCallbackList replies;
/* Address used for connect() */
struct sockaddr *saddr;
size_t addrlen;
/* Subscription callbacks */ /* Subscription callbacks */
struct { struct {
redisCallbackList invalid; redisCallbackList invalid;
......
#ifndef __HIREDIS_FMACRO_H #ifndef __HIREDIS_FMACRO_H
#define __HIREDIS_FMACRO_H #define __HIREDIS_FMACRO_H
#if defined(__linux__)
#define _BSD_SOURCE
#define _DEFAULT_SOURCE
#endif
#if defined(__CYGWIN__)
#include <sys/cdefs.h>
#endif
#if defined(__sun__)
#define _POSIX_C_SOURCE 200112L
#else
#if !(defined(__APPLE__) && defined(__MACH__)) && !(defined(__FreeBSD__))
#define _XOPEN_SOURCE 600 #define _XOPEN_SOURCE 600
#endif #define _POSIX_C_SOURCE 200112L
#endif
#if defined(__APPLE__) && defined(__MACH__) #if defined(__APPLE__) && defined(__MACH__)
#define _OSX /* Enable TCP_KEEPALIVE */
#define _DARWIN_C_SOURCE
#endif #endif
#endif #endif
...@@ -47,7 +47,9 @@ static redisReply *createReplyObject(int type); ...@@ -47,7 +47,9 @@ static redisReply *createReplyObject(int type);
static void *createStringObject(const redisReadTask *task, char *str, size_t len); static void *createStringObject(const redisReadTask *task, char *str, size_t len);
static void *createArrayObject(const redisReadTask *task, int elements); static void *createArrayObject(const redisReadTask *task, int elements);
static void *createIntegerObject(const redisReadTask *task, long long value); static void *createIntegerObject(const redisReadTask *task, long long value);
static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len);
static void *createNilObject(const redisReadTask *task); static void *createNilObject(const redisReadTask *task);
static void *createBoolObject(const redisReadTask *task, int bval);
/* Default set of functions to build the reply. Keep in mind that such a /* Default set of functions to build the reply. Keep in mind that such a
* function returning NULL is interpreted as OOM. */ * function returning NULL is interpreted as OOM. */
...@@ -55,7 +57,9 @@ static redisReplyObjectFunctions defaultFunctions = { ...@@ -55,7 +57,9 @@ static redisReplyObjectFunctions defaultFunctions = {
createStringObject, createStringObject,
createArrayObject, createArrayObject,
createIntegerObject, createIntegerObject,
createDoubleObject,
createNilObject, createNilObject,
createBoolObject,
freeReplyObject freeReplyObject
}; };
...@@ -82,9 +86,10 @@ void freeReplyObject(void *reply) { ...@@ -82,9 +86,10 @@ void freeReplyObject(void *reply) {
case REDIS_REPLY_INTEGER: case REDIS_REPLY_INTEGER:
break; /* Nothing to free */ break; /* Nothing to free */
case REDIS_REPLY_ARRAY: case REDIS_REPLY_ARRAY:
case REDIS_REPLY_MAP:
case REDIS_REPLY_SET:
if (r->element != NULL) { if (r->element != NULL) {
for (j = 0; j < r->elements; j++) for (j = 0; j < r->elements; j++)
if (r->element[j] != NULL)
freeReplyObject(r->element[j]); freeReplyObject(r->element[j]);
free(r->element); free(r->element);
} }
...@@ -92,7 +97,7 @@ void freeReplyObject(void *reply) { ...@@ -92,7 +97,7 @@ void freeReplyObject(void *reply) {
case REDIS_REPLY_ERROR: case REDIS_REPLY_ERROR:
case REDIS_REPLY_STATUS: case REDIS_REPLY_STATUS:
case REDIS_REPLY_STRING: case REDIS_REPLY_STRING:
if (r->str != NULL) case REDIS_REPLY_DOUBLE:
free(r->str); free(r->str);
break; break;
} }
...@@ -125,7 +130,9 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len ...@@ -125,7 +130,9 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
if (task->parent) { if (task->parent) {
parent = task->parent->obj; parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY); assert(parent->type == REDIS_REPLY_ARRAY ||
parent->type == REDIS_REPLY_MAP ||
parent->type == REDIS_REPLY_SET);
parent->element[task->idx] = r; parent->element[task->idx] = r;
} }
return r; return r;
...@@ -134,7 +141,7 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len ...@@ -134,7 +141,7 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
static void *createArrayObject(const redisReadTask *task, int elements) { static void *createArrayObject(const redisReadTask *task, int elements) {
redisReply *r, *parent; redisReply *r, *parent;
r = createReplyObject(REDIS_REPLY_ARRAY); r = createReplyObject(task->type);
if (r == NULL) if (r == NULL)
return NULL; return NULL;
...@@ -150,7 +157,9 @@ static void *createArrayObject(const redisReadTask *task, int elements) { ...@@ -150,7 +157,9 @@ static void *createArrayObject(const redisReadTask *task, int elements) {
if (task->parent) { if (task->parent) {
parent = task->parent->obj; parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY); assert(parent->type == REDIS_REPLY_ARRAY ||
parent->type == REDIS_REPLY_MAP ||
parent->type == REDIS_REPLY_SET);
parent->element[task->idx] = r; parent->element[task->idx] = r;
} }
return r; return r;
...@@ -167,7 +176,41 @@ static void *createIntegerObject(const redisReadTask *task, long long value) { ...@@ -167,7 +176,41 @@ static void *createIntegerObject(const redisReadTask *task, long long value) {
if (task->parent) { if (task->parent) {
parent = task->parent->obj; parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY); assert(parent->type == REDIS_REPLY_ARRAY ||
parent->type == REDIS_REPLY_MAP ||
parent->type == REDIS_REPLY_SET);
parent->element[task->idx] = r;
}
return r;
}
static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len) {
redisReply *r, *parent;
r = createReplyObject(REDIS_REPLY_DOUBLE);
if (r == NULL)
return NULL;
r->dval = value;
r->str = malloc(len+1);
if (r->str == NULL) {
freeReplyObject(r);
return NULL;
}
/* The double reply also has the original protocol string representing a
* double as a null terminated string. This way the caller does not need
* to format back for string conversion, especially since Redis does efforts
* to make the string more human readable avoiding the calssical double
* decimal string conversion artifacts. */
memcpy(r->str, str, len);
r->str[len] = '\0';
if (task->parent) {
parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY ||
parent->type == REDIS_REPLY_MAP ||
parent->type == REDIS_REPLY_SET);
parent->element[task->idx] = r; parent->element[task->idx] = r;
} }
return r; return r;
...@@ -182,7 +225,28 @@ static void *createNilObject(const redisReadTask *task) { ...@@ -182,7 +225,28 @@ static void *createNilObject(const redisReadTask *task) {
if (task->parent) { if (task->parent) {
parent = task->parent->obj; parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY); assert(parent->type == REDIS_REPLY_ARRAY ||
parent->type == REDIS_REPLY_MAP ||
parent->type == REDIS_REPLY_SET);
parent->element[task->idx] = r;
}
return r;
}
static void *createBoolObject(const redisReadTask *task, int bval) {
redisReply *r, *parent;
r = createReplyObject(REDIS_REPLY_BOOL);
if (r == NULL)
return NULL;
r->integer = bval != 0;
if (task->parent) {
parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY ||
parent->type == REDIS_REPLY_MAP ||
parent->type == REDIS_REPLY_SET);
parent->element[task->idx] = r; parent->element[task->idx] = r;
} }
return r; return r;
...@@ -432,10 +496,6 @@ cleanup: ...@@ -432,10 +496,6 @@ cleanup:
} }
sdsfree(curarg); sdsfree(curarg);
/* No need to check cmd since it is the last statement that can fail,
* but do it anyway to be as defensive as possible. */
if (cmd != NULL)
free(cmd); free(cmd);
return error_type; return error_type;
...@@ -581,7 +641,7 @@ void __redisSetError(redisContext *c, int type, const char *str) { ...@@ -581,7 +641,7 @@ void __redisSetError(redisContext *c, int type, const char *str) {
} else { } else {
/* Only REDIS_ERR_IO may lack a description! */ /* Only REDIS_ERR_IO may lack a description! */
assert(type == REDIS_ERR_IO); assert(type == REDIS_ERR_IO);
__redis_strerror_r(errno, c->errstr, sizeof(c->errstr)); strerror_r(errno, c->errstr, sizeof(c->errstr));
} }
} }
...@@ -596,14 +656,8 @@ static redisContext *redisContextInit(void) { ...@@ -596,14 +656,8 @@ static redisContext *redisContextInit(void) {
if (c == NULL) if (c == NULL)
return NULL; return NULL;
c->err = 0;
c->errstr[0] = '\0';
c->obuf = sdsempty(); c->obuf = sdsempty();
c->reader = redisReaderCreate(); c->reader = redisReaderCreate();
c->tcp.host = NULL;
c->tcp.source_addr = NULL;
c->unix_sock.path = NULL;
c->timeout = NULL;
if (c->obuf == NULL || c->reader == NULL) { if (c->obuf == NULL || c->reader == NULL) {
redisFree(c); redisFree(c);
...@@ -618,18 +672,14 @@ void redisFree(redisContext *c) { ...@@ -618,18 +672,14 @@ void redisFree(redisContext *c) {
return; return;
if (c->fd > 0) if (c->fd > 0)
close(c->fd); close(c->fd);
if (c->obuf != NULL)
sdsfree(c->obuf); sdsfree(c->obuf);
if (c->reader != NULL)
redisReaderFree(c->reader); redisReaderFree(c->reader);
if (c->tcp.host)
free(c->tcp.host); free(c->tcp.host);
if (c->tcp.source_addr)
free(c->tcp.source_addr); free(c->tcp.source_addr);
if (c->unix_sock.path)
free(c->unix_sock.path); free(c->unix_sock.path);
if (c->timeout)
free(c->timeout); free(c->timeout);
free(c->saddr);
free(c); free(c);
} }
...@@ -710,6 +760,8 @@ redisContext *redisConnectNonBlock(const char *ip, int port) { ...@@ -710,6 +760,8 @@ redisContext *redisConnectNonBlock(const char *ip, int port) {
redisContext *redisConnectBindNonBlock(const char *ip, int port, redisContext *redisConnectBindNonBlock(const char *ip, int port,
const char *source_addr) { const char *source_addr) {
redisContext *c = redisContextInit(); redisContext *c = redisContextInit();
if (c == NULL)
return NULL;
c->flags &= ~REDIS_BLOCK; c->flags &= ~REDIS_BLOCK;
redisContextConnectBindTcp(c,ip,port,NULL,source_addr); redisContextConnectBindTcp(c,ip,port,NULL,source_addr);
return c; return c;
...@@ -718,6 +770,8 @@ redisContext *redisConnectBindNonBlock(const char *ip, int port, ...@@ -718,6 +770,8 @@ redisContext *redisConnectBindNonBlock(const char *ip, int port,
redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
const char *source_addr) { const char *source_addr) {
redisContext *c = redisContextInit(); redisContext *c = redisContextInit();
if (c == NULL)
return NULL;
c->flags &= ~REDIS_BLOCK; c->flags &= ~REDIS_BLOCK;
c->flags |= REDIS_REUSEADDR; c->flags |= REDIS_REUSEADDR;
redisContextConnectBindTcp(c,ip,port,NULL,source_addr); redisContextConnectBindTcp(c,ip,port,NULL,source_addr);
...@@ -789,7 +843,7 @@ int redisEnableKeepAlive(redisContext *c) { ...@@ -789,7 +843,7 @@ int redisEnableKeepAlive(redisContext *c) {
/* Use this function to handle a read event on the descriptor. It will try /* Use this function to handle a read event on the descriptor. It will try
* and read some bytes from the socket and feed them to the reply parser. * and read some bytes from the socket and feed them to the reply parser.
* *
* After this function is called, you may use redisContextReadReply to * After this function is called, you may use redisGetReplyFromReader to
* see if there is a reply available. */ * see if there is a reply available. */
int redisBufferRead(redisContext *c) { int redisBufferRead(redisContext *c) {
char buf[1024*16]; char buf[1024*16];
...@@ -1007,9 +1061,8 @@ void *redisvCommand(redisContext *c, const char *format, va_list ap) { ...@@ -1007,9 +1061,8 @@ void *redisvCommand(redisContext *c, const char *format, va_list ap) {
void *redisCommand(redisContext *c, const char *format, ...) { void *redisCommand(redisContext *c, const char *format, ...) {
va_list ap; va_list ap;
void *reply = NULL;
va_start(ap,format); va_start(ap,format);
reply = redisvCommand(c,format,ap); void *reply = redisvCommand(c,format,ap);
va_end(ap); va_end(ap);
return reply; return reply;
} }
......
...@@ -40,9 +40,9 @@ ...@@ -40,9 +40,9 @@
#include "sds.h" /* for sds */ #include "sds.h" /* for sds */
#define HIREDIS_MAJOR 0 #define HIREDIS_MAJOR 0
#define HIREDIS_MINOR 13 #define HIREDIS_MINOR 14
#define HIREDIS_PATCH 3 #define HIREDIS_PATCH 0
#define HIREDIS_SONAME 0.13 #define HIREDIS_SONAME 0.14
/* Connection type can be blocking or non-blocking and is set in the /* Connection type can be blocking or non-blocking and is set in the
* least significant bit of the flags field in redisContext. */ * least significant bit of the flags field in redisContext. */
...@@ -80,30 +80,6 @@ ...@@ -80,30 +80,6 @@
* SO_REUSEADDR is being used. */ * SO_REUSEADDR is being used. */
#define REDIS_CONNECT_RETRIES 10 #define REDIS_CONNECT_RETRIES 10
/* strerror_r has two completely different prototypes and behaviors
* depending on system issues, so we need to operate on the error buffer
* differently depending on which strerror_r we're using. */
#ifndef _GNU_SOURCE
/* "regular" POSIX strerror_r that does the right thing. */
#define __redis_strerror_r(errno, buf, len) \
do { \
strerror_r((errno), (buf), (len)); \
} while (0)
#else
/* "bad" GNU strerror_r we need to clean up after. */
#define __redis_strerror_r(errno, buf, len) \
do { \
char *err_str = strerror_r((errno), (buf), (len)); \
/* If return value _isn't_ the start of the buffer we passed in, \
* then GNU strerror_r returned an internal static buffer and we \
* need to copy the result into our private buffer. */ \
if (err_str != (buf)) { \
strncpy((buf), err_str, ((len) - 1)); \
buf[(len)-1] = '\0'; \
} \
} while (0)
#endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
...@@ -112,8 +88,10 @@ extern "C" { ...@@ -112,8 +88,10 @@ extern "C" {
typedef struct redisReply { typedef struct redisReply {
int type; /* REDIS_REPLY_* */ int type; /* REDIS_REPLY_* */
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */ long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
double dval; /* The double when type is REDIS_REPLY_DOUBLE */
size_t len; /* Length of string */ size_t len; /* Length of string */
char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */ char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING
and REDIS_REPLY_DOUBLE (in additionl to dval). */
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */ struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
} redisReply; } redisReply;
...@@ -158,6 +136,9 @@ typedef struct redisContext { ...@@ -158,6 +136,9 @@ typedef struct redisContext {
char *path; char *path;
} unix_sock; } unix_sock;
/* For non-blocking connect */
struct sockadr *saddr;
size_t addrlen;
} redisContext; } redisContext;
redisContext *redisConnect(const char *ip, int port); redisContext *redisConnect(const char *ip, int port);
......
...@@ -65,12 +65,13 @@ static void redisContextCloseFd(redisContext *c) { ...@@ -65,12 +65,13 @@ static void redisContextCloseFd(redisContext *c) {
} }
static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) { static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
int errorno = errno; /* snprintf() may change errno */
char buf[128] = { 0 }; char buf[128] = { 0 };
size_t len = 0; size_t len = 0;
if (prefix != NULL) if (prefix != NULL)
len = snprintf(buf,sizeof(buf),"%s: ",prefix); len = snprintf(buf,sizeof(buf),"%s: ",prefix);
__redis_strerror_r(errno, (char *)(buf + len), sizeof(buf) - len); strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len);
__redisSetError(c,type,buf); __redisSetError(c,type,buf);
} }
...@@ -135,14 +136,13 @@ int redisKeepAlive(redisContext *c, int interval) { ...@@ -135,14 +136,13 @@ int redisKeepAlive(redisContext *c, int interval) {
val = interval; val = interval;
#ifdef _OSX #if defined(__APPLE__) && defined(__MACH__)
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) { if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
return REDIS_ERR; return REDIS_ERR;
} }
#else #else
#if defined(__GLIBC__) && !defined(__FreeBSD_kernel__) #if defined(__GLIBC__) && !defined(__FreeBSD_kernel__)
val = interval;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) { if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
return REDIS_ERR; return REDIS_ERR;
...@@ -221,8 +221,10 @@ static int redisContextWaitReady(redisContext *c, long msec) { ...@@ -221,8 +221,10 @@ static int redisContextWaitReady(redisContext *c, long msec) {
return REDIS_ERR; return REDIS_ERR;
} }
if (redisCheckSocketError(c) != REDIS_OK) if (redisCheckConnectDone(c, &res) != REDIS_OK || res == 0) {
redisCheckSocketError(c);
return REDIS_ERR; return REDIS_ERR;
}
return REDIS_OK; return REDIS_OK;
} }
...@@ -232,8 +234,28 @@ static int redisContextWaitReady(redisContext *c, long msec) { ...@@ -232,8 +234,28 @@ static int redisContextWaitReady(redisContext *c, long msec) {
return REDIS_ERR; return REDIS_ERR;
} }
int redisCheckConnectDone(redisContext *c, int *completed) {
int rc = connect(c->fd, (const struct sockaddr *)c->saddr, c->addrlen);
if (rc == 0) {
*completed = 1;
return REDIS_OK;
}
switch (errno) {
case EISCONN:
*completed = 1;
return REDIS_OK;
case EALREADY:
case EINPROGRESS:
case EWOULDBLOCK:
*completed = 0;
return REDIS_OK;
default:
return REDIS_ERR;
}
}
int redisCheckSocketError(redisContext *c) { int redisCheckSocketError(redisContext *c) {
int err = 0; int err = 0, errno_saved = errno;
socklen_t errlen = sizeof(err); socklen_t errlen = sizeof(err);
if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) { if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
...@@ -241,6 +263,10 @@ int redisCheckSocketError(redisContext *c) { ...@@ -241,6 +263,10 @@ int redisCheckSocketError(redisContext *c) {
return REDIS_ERR; return REDIS_ERR;
} }
if (err == 0) {
err = errno_saved;
}
if (err) { if (err) {
errno = err; errno = err;
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
...@@ -285,7 +311,6 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, ...@@ -285,7 +311,6 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
* This is a bit ugly, but atleast it works and doesn't leak memory. * This is a bit ugly, but atleast it works and doesn't leak memory.
**/ **/
if (c->tcp.host != addr) { if (c->tcp.host != addr) {
if (c->tcp.host)
free(c->tcp.host); free(c->tcp.host);
c->tcp.host = strdup(addr); c->tcp.host = strdup(addr);
...@@ -299,7 +324,6 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, ...@@ -299,7 +324,6 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
memcpy(c->timeout, timeout, sizeof(struct timeval)); memcpy(c->timeout, timeout, sizeof(struct timeval));
} }
} else { } else {
if (c->timeout)
free(c->timeout); free(c->timeout);
c->timeout = NULL; c->timeout = NULL;
} }
...@@ -356,6 +380,7 @@ addrretry: ...@@ -356,6 +380,7 @@ addrretry:
n = 1; n = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n, if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,
sizeof(n)) < 0) { sizeof(n)) < 0) {
freeaddrinfo(bservinfo);
goto error; goto error;
} }
} }
...@@ -374,12 +399,27 @@ addrretry: ...@@ -374,12 +399,27 @@ addrretry:
goto error; goto error;
} }
} }
/* For repeat connection */
if (c->saddr) {
free(c->saddr);
}
c->saddr = malloc(p->ai_addrlen);
memcpy(c->saddr, p->ai_addr, p->ai_addrlen);
c->addrlen = p->ai_addrlen;
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) { if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
if (errno == EHOSTUNREACH) { if (errno == EHOSTUNREACH) {
redisContextCloseFd(c); redisContextCloseFd(c);
continue; continue;
} else if (errno == EINPROGRESS && !blocking) { } else if (errno == EINPROGRESS) {
/* This is ok. */ if (blocking) {
goto wait_for_ready;
}
/* This is ok.
* Note that even when it's in blocking mode, we unset blocking
* for `connect()`
*/
} else if (errno == EADDRNOTAVAIL && reuseaddr) { } else if (errno == EADDRNOTAVAIL && reuseaddr) {
if (++reuses >= REDIS_CONNECT_RETRIES) { if (++reuses >= REDIS_CONNECT_RETRIES) {
goto error; goto error;
...@@ -388,6 +428,7 @@ addrretry: ...@@ -388,6 +428,7 @@ addrretry:
goto addrretry; goto addrretry;
} }
} else { } else {
wait_for_ready:
if (redisContextWaitReady(c,timeout_msec) != REDIS_OK) if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
goto error; goto error;
} }
...@@ -411,7 +452,10 @@ addrretry: ...@@ -411,7 +452,10 @@ addrretry:
error: error:
rv = REDIS_ERR; rv = REDIS_ERR;
end: end:
if(servinfo) {
freeaddrinfo(servinfo); freeaddrinfo(servinfo);
}
return rv; // Need to return REDIS_OK if alright return rv; // Need to return REDIS_OK if alright
} }
...@@ -431,7 +475,7 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time ...@@ -431,7 +475,7 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time
struct sockaddr_un sa; struct sockaddr_un sa;
long timeout_msec = -1; long timeout_msec = -1;
if (redisCreateSocket(c,AF_LOCAL) < 0) if (redisCreateSocket(c,AF_UNIX) < 0)
return REDIS_ERR; return REDIS_ERR;
if (redisSetBlocking(c,0) != REDIS_OK) if (redisSetBlocking(c,0) != REDIS_OK)
return REDIS_ERR; return REDIS_ERR;
...@@ -448,7 +492,6 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time ...@@ -448,7 +492,6 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time
memcpy(c->timeout, timeout, sizeof(struct timeval)); memcpy(c->timeout, timeout, sizeof(struct timeval));
} }
} else { } else {
if (c->timeout)
free(c->timeout); free(c->timeout);
c->timeout = NULL; c->timeout = NULL;
} }
...@@ -456,7 +499,7 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time ...@@ -456,7 +499,7 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time
if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK) if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)
return REDIS_ERR; return REDIS_ERR;
sa.sun_family = AF_LOCAL; sa.sun_family = AF_UNIX;
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1); strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
if (connect(c->fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) { if (connect(c->fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
if (errno == EINPROGRESS && !blocking) { if (errno == EINPROGRESS && !blocking) {
......
...@@ -37,10 +37,6 @@ ...@@ -37,10 +37,6 @@
#include "hiredis.h" #include "hiredis.h"
#if defined(__sun)
#define AF_LOCAL AF_UNIX
#endif
int redisCheckSocketError(redisContext *c); int redisCheckSocketError(redisContext *c);
int redisContextSetTimeout(redisContext *c, const struct timeval tv); int redisContextSetTimeout(redisContext *c, const struct timeval tv);
int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout); int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);
...@@ -49,5 +45,6 @@ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, ...@@ -49,5 +45,6 @@ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
const char *source_addr); const char *source_addr);
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout); int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);
int redisKeepAlive(redisContext *c, int interval); int redisKeepAlive(redisContext *c, int interval);
int redisCheckConnectDone(redisContext *c, int *completed);
#endif #endif
...@@ -29,7 +29,6 @@ ...@@ -29,7 +29,6 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "fmacros.h" #include "fmacros.h"
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -39,6 +38,8 @@ ...@@ -39,6 +38,8 @@
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <ctype.h> #include <ctype.h>
#include <limits.h>
#include <math.h>
#include "read.h" #include "read.h"
#include "sds.h" #include "sds.h"
...@@ -52,11 +53,9 @@ static void __redisReaderSetError(redisReader *r, int type, const char *str) { ...@@ -52,11 +53,9 @@ static void __redisReaderSetError(redisReader *r, int type, const char *str) {
} }
/* Clear input buffer on errors. */ /* Clear input buffer on errors. */
if (r->buf != NULL) {
sdsfree(r->buf); sdsfree(r->buf);
r->buf = NULL; r->buf = NULL;
r->pos = r->len = 0; r->pos = r->len = 0;
}
/* Reset task stack. */ /* Reset task stack. */
r->ridx = -1; r->ridx = -1;
...@@ -143,33 +142,79 @@ static char *seekNewline(char *s, size_t len) { ...@@ -143,33 +142,79 @@ static char *seekNewline(char *s, size_t len) {
return NULL; return NULL;
} }
/* Read a long long value starting at *s, under the assumption that it will be /* Convert a string into a long long. Returns REDIS_OK if the string could be
* terminated by \r\n. Ambiguously returns -1 for unexpected input. */ * parsed into a (non-overflowing) long long, REDIS_ERR otherwise. The value
static long long readLongLong(char *s) { * will be set to the parsed value when appropriate.
long long v = 0; *
int dec, mult = 1; * Note that this function demands that the string strictly represents
char c; * a long long: no spaces or other characters before or after the string
* representing the number are accepted, nor zeroes at the start if not
* for the string "0" representing the zero number.
*
* Because of its strictness, it is safe to use this function to check if
* you can convert a string into a long long, and obtain back the string
* from the number without any loss in the string representation. */
static int string2ll(const char *s, size_t slen, long long *value) {
const char *p = s;
size_t plen = 0;
int negative = 0;
unsigned long long v;
if (plen == slen)
return REDIS_ERR;
if (*s == '-') { /* Special case: first and only digit is 0. */
mult = -1; if (slen == 1 && p[0] == '0') {
s++; if (value != NULL) *value = 0;
} else if (*s == '+') { return REDIS_OK;
mult = 1;
s++;
} }
while ((c = *(s++)) != '\r') { if (p[0] == '-') {
dec = c - '0'; negative = 1;
if (dec >= 0 && dec < 10) { p++; plen++;
v *= 10;
v += dec; /* Abort on only a negative sign. */
if (plen == slen)
return REDIS_ERR;
}
/* First digit should be 1-9, otherwise the string should just be 0. */
if (p[0] >= '1' && p[0] <= '9') {
v = p[0]-'0';
p++; plen++;
} else if (p[0] == '0' && slen == 1) {
*value = 0;
return REDIS_OK;
} else { } else {
/* Should not happen... */ return REDIS_ERR;
return -1;
} }
while (plen < slen && p[0] >= '0' && p[0] <= '9') {
if (v > (ULLONG_MAX / 10)) /* Overflow. */
return REDIS_ERR;
v *= 10;
if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */
return REDIS_ERR;
v += p[0]-'0';
p++; plen++;
} }
return mult*v; /* Return if not all bytes were used. */
if (plen < slen)
return REDIS_ERR;
if (negative) {
if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */
return REDIS_ERR;
if (value != NULL) *value = -v;
} else {
if (v > LLONG_MAX) /* Overflow. */
return REDIS_ERR;
if (value != NULL) *value = v;
}
return REDIS_OK;
} }
static char *readLine(redisReader *r, int *_len) { static char *readLine(redisReader *r, int *_len) {
...@@ -198,7 +243,9 @@ static void moveToNextTask(redisReader *r) { ...@@ -198,7 +243,9 @@ static void moveToNextTask(redisReader *r) {
cur = &(r->rstack[r->ridx]); cur = &(r->rstack[r->ridx]);
prv = &(r->rstack[r->ridx-1]); prv = &(r->rstack[r->ridx-1]);
assert(prv->type == REDIS_REPLY_ARRAY); assert(prv->type == REDIS_REPLY_ARRAY ||
prv->type == REDIS_REPLY_MAP ||
prv->type == REDIS_REPLY_SET);
if (cur->idx == prv->elements-1) { if (cur->idx == prv->elements-1) {
r->ridx--; r->ridx--;
} else { } else {
...@@ -220,10 +267,58 @@ static int processLineItem(redisReader *r) { ...@@ -220,10 +267,58 @@ static int processLineItem(redisReader *r) {
if ((p = readLine(r,&len)) != NULL) { if ((p = readLine(r,&len)) != NULL) {
if (cur->type == REDIS_REPLY_INTEGER) { if (cur->type == REDIS_REPLY_INTEGER) {
if (r->fn && r->fn->createInteger) if (r->fn && r->fn->createInteger) {
obj = r->fn->createInteger(cur,readLongLong(p)); long long v;
else if (string2ll(p, len, &v) == REDIS_ERR) {
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
"Bad integer value");
return REDIS_ERR;
}
obj = r->fn->createInteger(cur,v);
} else {
obj = (void*)REDIS_REPLY_INTEGER; obj = (void*)REDIS_REPLY_INTEGER;
}
} else if (cur->type == REDIS_REPLY_DOUBLE) {
if (r->fn && r->fn->createDouble) {
char buf[326], *eptr;
double d;
if ((size_t)len >= sizeof(buf)) {
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
"Double value is too large");
return REDIS_ERR;
}
memcpy(buf,p,len);
buf[len] = '\0';
if (strcasecmp(buf,",inf") == 0) {
d = 1.0/0.0; /* Positive infinite. */
} else if (strcasecmp(buf,",-inf") == 0) {
d = -1.0/0.0; /* Nevative infinite. */
} else {
d = strtod((char*)buf,&eptr);
if (buf[0] == '\0' || eptr[0] != '\0' || isnan(d)) {
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
"Bad double value");
return REDIS_ERR;
}
}
obj = r->fn->createDouble(cur,d,buf,len);
} else {
obj = (void*)REDIS_REPLY_DOUBLE;
}
} else if (cur->type == REDIS_REPLY_NIL) {
if (r->fn && r->fn->createNil)
obj = r->fn->createNil(cur);
else
obj = (void*)REDIS_REPLY_NIL;
} else if (cur->type == REDIS_REPLY_BOOL) {
int bval = p[0] == 't' || p[0] == 'T';
if (r->fn && r->fn->createBool)
obj = r->fn->createBool(cur,bval);
else
obj = (void*)REDIS_REPLY_BOOL;
} else { } else {
/* Type will be error or status. */ /* Type will be error or status. */
if (r->fn && r->fn->createString) if (r->fn && r->fn->createString)
...@@ -250,7 +345,7 @@ static int processBulkItem(redisReader *r) { ...@@ -250,7 +345,7 @@ static int processBulkItem(redisReader *r) {
redisReadTask *cur = &(r->rstack[r->ridx]); redisReadTask *cur = &(r->rstack[r->ridx]);
void *obj = NULL; void *obj = NULL;
char *p, *s; char *p, *s;
long len; long long len;
unsigned long bytelen; unsigned long bytelen;
int success = 0; int success = 0;
...@@ -259,9 +354,20 @@ static int processBulkItem(redisReader *r) { ...@@ -259,9 +354,20 @@ static int processBulkItem(redisReader *r) {
if (s != NULL) { if (s != NULL) {
p = r->buf+r->pos; p = r->buf+r->pos;
bytelen = s-(r->buf+r->pos)+2; /* include \r\n */ bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
len = readLongLong(p);
if (len < 0) { if (string2ll(p, bytelen - 2, &len) == REDIS_ERR) {
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
"Bad bulk string length");
return REDIS_ERR;
}
if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) {
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
"Bulk string length out of range");
return REDIS_ERR;
}
if (len == -1) {
/* The nil object can always be created. */ /* The nil object can always be created. */
if (r->fn && r->fn->createNil) if (r->fn && r->fn->createNil)
obj = r->fn->createNil(cur); obj = r->fn->createNil(cur);
...@@ -299,12 +405,13 @@ static int processBulkItem(redisReader *r) { ...@@ -299,12 +405,13 @@ static int processBulkItem(redisReader *r) {
return REDIS_ERR; return REDIS_ERR;
} }
static int processMultiBulkItem(redisReader *r) { /* Process the array, map and set types. */
static int processAggregateItem(redisReader *r) {
redisReadTask *cur = &(r->rstack[r->ridx]); redisReadTask *cur = &(r->rstack[r->ridx]);
void *obj; void *obj;
char *p; char *p;
long elements; long long elements;
int root = 0; int root = 0, len;
/* Set error for nested multi bulks with depth > 7 */ /* Set error for nested multi bulks with depth > 7 */
if (r->ridx == 8) { if (r->ridx == 8) {
...@@ -313,10 +420,21 @@ static int processMultiBulkItem(redisReader *r) { ...@@ -313,10 +420,21 @@ static int processMultiBulkItem(redisReader *r) {
return REDIS_ERR; return REDIS_ERR;
} }
if ((p = readLine(r,NULL)) != NULL) { if ((p = readLine(r,&len)) != NULL) {
elements = readLongLong(p); if (string2ll(p, len, &elements) == REDIS_ERR) {
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
"Bad multi-bulk length");
return REDIS_ERR;
}
root = (r->ridx == 0); root = (r->ridx == 0);
if (elements < -1 || elements > INT_MAX) {
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
"Multi-bulk length out of range");
return REDIS_ERR;
}
if (elements == -1) { if (elements == -1) {
if (r->fn && r->fn->createNil) if (r->fn && r->fn->createNil)
obj = r->fn->createNil(cur); obj = r->fn->createNil(cur);
...@@ -330,10 +448,12 @@ static int processMultiBulkItem(redisReader *r) { ...@@ -330,10 +448,12 @@ static int processMultiBulkItem(redisReader *r) {
moveToNextTask(r); moveToNextTask(r);
} else { } else {
if (cur->type == REDIS_REPLY_MAP) elements *= 2;
if (r->fn && r->fn->createArray) if (r->fn && r->fn->createArray)
obj = r->fn->createArray(cur,elements); obj = r->fn->createArray(cur,elements);
else else
obj = (void*)REDIS_REPLY_ARRAY; obj = (void*)(long)cur->type;
if (obj == NULL) { if (obj == NULL) {
__redisReaderSetErrorOOM(r); __redisReaderSetErrorOOM(r);
...@@ -381,12 +501,27 @@ static int processItem(redisReader *r) { ...@@ -381,12 +501,27 @@ static int processItem(redisReader *r) {
case ':': case ':':
cur->type = REDIS_REPLY_INTEGER; cur->type = REDIS_REPLY_INTEGER;
break; break;
case ',':
cur->type = REDIS_REPLY_DOUBLE;
break;
case '_':
cur->type = REDIS_REPLY_NIL;
break;
case '$': case '$':
cur->type = REDIS_REPLY_STRING; cur->type = REDIS_REPLY_STRING;
break; break;
case '*': case '*':
cur->type = REDIS_REPLY_ARRAY; cur->type = REDIS_REPLY_ARRAY;
break; break;
case '%':
cur->type = REDIS_REPLY_MAP;
break;
case '~':
cur->type = REDIS_REPLY_SET;
break;
case '#':
cur->type = REDIS_REPLY_BOOL;
break;
default: default:
__redisReaderSetErrorProtocolByte(r,*p); __redisReaderSetErrorProtocolByte(r,*p);
return REDIS_ERR; return REDIS_ERR;
...@@ -402,11 +537,16 @@ static int processItem(redisReader *r) { ...@@ -402,11 +537,16 @@ static int processItem(redisReader *r) {
case REDIS_REPLY_ERROR: case REDIS_REPLY_ERROR:
case REDIS_REPLY_STATUS: case REDIS_REPLY_STATUS:
case REDIS_REPLY_INTEGER: case REDIS_REPLY_INTEGER:
case REDIS_REPLY_DOUBLE:
case REDIS_REPLY_NIL:
case REDIS_REPLY_BOOL:
return processLineItem(r); return processLineItem(r);
case REDIS_REPLY_STRING: case REDIS_REPLY_STRING:
return processBulkItem(r); return processBulkItem(r);
case REDIS_REPLY_ARRAY: case REDIS_REPLY_ARRAY:
return processMultiBulkItem(r); case REDIS_REPLY_MAP:
case REDIS_REPLY_SET:
return processAggregateItem(r);
default: default:
assert(NULL); assert(NULL);
return REDIS_ERR; /* Avoid warning. */ return REDIS_ERR; /* Avoid warning. */
...@@ -416,12 +556,10 @@ static int processItem(redisReader *r) { ...@@ -416,12 +556,10 @@ static int processItem(redisReader *r) {
redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) { redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
redisReader *r; redisReader *r;
r = calloc(sizeof(redisReader),1); r = calloc(1,sizeof(redisReader));
if (r == NULL) if (r == NULL)
return NULL; return NULL;
r->err = 0;
r->errstr[0] = '\0';
r->fn = fn; r->fn = fn;
r->buf = sdsempty(); r->buf = sdsempty();
r->maxbuf = REDIS_READER_MAX_BUF; r->maxbuf = REDIS_READER_MAX_BUF;
...@@ -435,9 +573,10 @@ redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) { ...@@ -435,9 +573,10 @@ redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
} }
void redisReaderFree(redisReader *r) { void redisReaderFree(redisReader *r) {
if (r == NULL)
return;
if (r->reply != NULL && r->fn && r->fn->freeObject) if (r->reply != NULL && r->fn && r->fn->freeObject)
r->fn->freeObject(r->reply); r->fn->freeObject(r->reply);
if (r->buf != NULL)
sdsfree(r->buf); sdsfree(r->buf);
free(r); free(r);
} }
......
...@@ -53,6 +53,14 @@ ...@@ -53,6 +53,14 @@
#define REDIS_REPLY_NIL 4 #define REDIS_REPLY_NIL 4
#define REDIS_REPLY_STATUS 5 #define REDIS_REPLY_STATUS 5
#define REDIS_REPLY_ERROR 6 #define REDIS_REPLY_ERROR 6
#define REDIS_REPLY_DOUBLE 7
#define REDIS_REPLY_BOOL 8
#define REDIS_REPLY_VERB 9
#define REDIS_REPLY_MAP 9
#define REDIS_REPLY_SET 10
#define REDIS_REPLY_ATTR 11
#define REDIS_REPLY_PUSH 12
#define REDIS_REPLY_BIGNUM 13
#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */ #define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */
...@@ -73,7 +81,9 @@ typedef struct redisReplyObjectFunctions { ...@@ -73,7 +81,9 @@ typedef struct redisReplyObjectFunctions {
void *(*createString)(const redisReadTask*, char*, size_t); void *(*createString)(const redisReadTask*, char*, size_t);
void *(*createArray)(const redisReadTask*, int); void *(*createArray)(const redisReadTask*, int);
void *(*createInteger)(const redisReadTask*, long long); void *(*createInteger)(const redisReadTask*, long long);
void *(*createDouble)(const redisReadTask*, double, char*, size_t);
void *(*createNil)(const redisReadTask*); void *(*createNil)(const redisReadTask*);
void *(*createBool)(const redisReadTask*, int);
void (*freeObject)(void*); void (*freeObject)(void*);
} redisReplyObjectFunctions; } redisReplyObjectFunctions;
......
...@@ -219,7 +219,10 @@ sds sdsMakeRoomFor(sds s, size_t addlen) { ...@@ -219,7 +219,10 @@ sds sdsMakeRoomFor(sds s, size_t addlen) {
hdrlen = sdsHdrSize(type); hdrlen = sdsHdrSize(type);
if (oldtype==type) { if (oldtype==type) {
newsh = s_realloc(sh, hdrlen+newlen+1); newsh = s_realloc(sh, hdrlen+newlen+1);
if (newsh == NULL) return NULL; if (newsh == NULL) {
s_free(sh);
return NULL;
}
s = (char*)newsh+hdrlen; s = (char*)newsh+hdrlen;
} else { } else {
/* Since the header size changes, need to move the string forward, /* Since the header size changes, need to move the string forward,
...@@ -592,6 +595,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) { ...@@ -592,6 +595,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
/* Make sure there is always space for at least 1 char. */ /* Make sure there is always space for at least 1 char. */
if (sdsavail(s)==0) { if (sdsavail(s)==0) {
s = sdsMakeRoomFor(s,1); s = sdsMakeRoomFor(s,1);
if (s == NULL) goto fmt_error;
} }
switch(*f) { switch(*f) {
...@@ -605,6 +609,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) { ...@@ -605,6 +609,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
l = (next == 's') ? strlen(str) : sdslen(str); l = (next == 's') ? strlen(str) : sdslen(str);
if (sdsavail(s) < l) { if (sdsavail(s) < l) {
s = sdsMakeRoomFor(s,l); s = sdsMakeRoomFor(s,l);
if (s == NULL) goto fmt_error;
} }
memcpy(s+i,str,l); memcpy(s+i,str,l);
sdsinclen(s,l); sdsinclen(s,l);
...@@ -621,6 +626,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) { ...@@ -621,6 +626,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
l = sdsll2str(buf,num); l = sdsll2str(buf,num);
if (sdsavail(s) < l) { if (sdsavail(s) < l) {
s = sdsMakeRoomFor(s,l); s = sdsMakeRoomFor(s,l);
if (s == NULL) goto fmt_error;
} }
memcpy(s+i,buf,l); memcpy(s+i,buf,l);
sdsinclen(s,l); sdsinclen(s,l);
...@@ -638,6 +644,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) { ...@@ -638,6 +644,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
l = sdsull2str(buf,unum); l = sdsull2str(buf,unum);
if (sdsavail(s) < l) { if (sdsavail(s) < l) {
s = sdsMakeRoomFor(s,l); s = sdsMakeRoomFor(s,l);
if (s == NULL) goto fmt_error;
} }
memcpy(s+i,buf,l); memcpy(s+i,buf,l);
sdsinclen(s,l); sdsinclen(s,l);
...@@ -662,6 +669,10 @@ sds sdscatfmt(sds s, char const *fmt, ...) { ...@@ -662,6 +669,10 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
/* Add null-term */ /* Add null-term */
s[i] = '\0'; s[i] = '\0';
return s; return s;
fmt_error:
va_end(ap);
return NULL;
} }
/* Remove the part of the string from left and from right composed just of /* Remove the part of the string from left and from right composed just of
...@@ -1018,10 +1029,18 @@ sds *sdssplitargs(const char *line, int *argc) { ...@@ -1018,10 +1029,18 @@ sds *sdssplitargs(const char *line, int *argc) {
if (*p) p++; if (*p) p++;
} }
/* add the token to the vector */ /* add the token to the vector */
vector = s_realloc(vector,((*argc)+1)*sizeof(char*)); {
char **new_vector = s_realloc(vector,((*argc)+1)*sizeof(char*));
if (new_vector == NULL) {
s_free(vector);
return NULL;
}
vector = new_vector;
vector[*argc] = current; vector[*argc] = current;
(*argc)++; (*argc)++;
current = NULL; current = NULL;
}
} else { } else {
/* Even on empty input string return something not NULL. */ /* Even on empty input string return something not NULL. */
if (vector == NULL) vector = s_malloc(sizeof(void*)); if (vector == NULL) vector = s_malloc(sizeof(void*));
......
...@@ -3,7 +3,9 @@ ...@@ -3,7 +3,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <sys/socket.h>
#include <sys/time.h> #include <sys/time.h>
#include <netdb.h>
#include <assert.h> #include <assert.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h> #include <signal.h>
...@@ -91,7 +93,7 @@ static int disconnect(redisContext *c, int keep_fd) { ...@@ -91,7 +93,7 @@ static int disconnect(redisContext *c, int keep_fd) {
return -1; return -1;
} }
static redisContext *connect(struct config config) { static redisContext *do_connect(struct config config) {
redisContext *c = NULL; redisContext *c = NULL;
if (config.type == CONN_TCP) { if (config.type == CONN_TCP) {
...@@ -248,7 +250,7 @@ static void test_append_formatted_commands(struct config config) { ...@@ -248,7 +250,7 @@ static void test_append_formatted_commands(struct config config) {
char *cmd; char *cmd;
int len; int len;
c = connect(config); c = do_connect(config);
test("Append format command: "); test("Append format command: ");
...@@ -302,6 +304,82 @@ static void test_reply_reader(void) { ...@@ -302,6 +304,82 @@ static void test_reply_reader(void) {
strncasecmp(reader->errstr,"No support for",14) == 0); strncasecmp(reader->errstr,"No support for",14) == 0);
redisReaderFree(reader); redisReaderFree(reader);
test("Correctly parses LLONG_MAX: ");
reader = redisReaderCreate();
redisReaderFeed(reader, ":9223372036854775807\r\n",22);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_OK &&
((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&
((redisReply*)reply)->integer == LLONG_MAX);
freeReplyObject(reply);
redisReaderFree(reader);
test("Set error when > LLONG_MAX: ");
reader = redisReaderCreate();
redisReaderFeed(reader, ":9223372036854775808\r\n",22);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_ERR &&
strcasecmp(reader->errstr,"Bad integer value") == 0);
freeReplyObject(reply);
redisReaderFree(reader);
test("Correctly parses LLONG_MIN: ");
reader = redisReaderCreate();
redisReaderFeed(reader, ":-9223372036854775808\r\n",23);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_OK &&
((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&
((redisReply*)reply)->integer == LLONG_MIN);
freeReplyObject(reply);
redisReaderFree(reader);
test("Set error when < LLONG_MIN: ");
reader = redisReaderCreate();
redisReaderFeed(reader, ":-9223372036854775809\r\n",23);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_ERR &&
strcasecmp(reader->errstr,"Bad integer value") == 0);
freeReplyObject(reply);
redisReaderFree(reader);
test("Set error when array < -1: ");
reader = redisReaderCreate();
redisReaderFeed(reader, "*-2\r\n+asdf\r\n",12);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_ERR &&
strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0);
freeReplyObject(reply);
redisReaderFree(reader);
test("Set error when bulk < -1: ");
reader = redisReaderCreate();
redisReaderFeed(reader, "$-2\r\nasdf\r\n",11);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_ERR &&
strcasecmp(reader->errstr,"Bulk string length out of range") == 0);
freeReplyObject(reply);
redisReaderFree(reader);
test("Set error when array > INT_MAX: ");
reader = redisReaderCreate();
redisReaderFeed(reader, "*9223372036854775807\r\n+asdf\r\n",29);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_ERR &&
strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0);
freeReplyObject(reply);
redisReaderFree(reader);
#if LLONG_MAX > SIZE_MAX
test("Set error when bulk > SIZE_MAX: ");
reader = redisReaderCreate();
redisReaderFeed(reader, "$9223372036854775807\r\nasdf\r\n",28);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_ERR &&
strcasecmp(reader->errstr,"Bulk string length out of range") == 0);
freeReplyObject(reply);
redisReaderFree(reader);
#endif
test("Works with NULL functions for reply: "); test("Works with NULL functions for reply: ");
reader = redisReaderCreate(); reader = redisReaderCreate();
reader->fn = NULL; reader->fn = NULL;
...@@ -358,18 +436,32 @@ static void test_free_null(void) { ...@@ -358,18 +436,32 @@ static void test_free_null(void) {
static void test_blocking_connection_errors(void) { static void test_blocking_connection_errors(void) {
redisContext *c; redisContext *c;
struct addrinfo hints = {.ai_family = AF_INET};
struct addrinfo *ai_tmp = NULL;
const char *bad_domain = "idontexist.com";
int rv = getaddrinfo(bad_domain, "6379", &hints, &ai_tmp);
if (rv != 0) {
// Address does *not* exist
test("Returns error when host cannot be resolved: "); test("Returns error when host cannot be resolved: ");
c = redisConnect((char*)"idontexist.test", 6379); // First see if this domain name *actually* resolves to NXDOMAIN
test_cond(c->err == REDIS_ERR_OTHER && c = redisConnect("dontexist.com", 6379);
(strcmp(c->errstr,"Name or service not known") == 0 || test_cond(
strcmp(c->errstr,"Can't resolve: idontexist.test") == 0 || c->err == REDIS_ERR_OTHER &&
strcmp(c->errstr,"nodename nor servname provided, or not known") == 0 || (strcmp(c->errstr, "Name or service not known") == 0 ||
strcmp(c->errstr,"No address associated with hostname") == 0 || strcmp(c->errstr, "Can't resolve: sadkfjaskfjsa.com") == 0 ||
strcmp(c->errstr,"Temporary failure in name resolution") == 0 || strcmp(c->errstr,
strcmp(c->errstr,"hostname nor servname provided, or not known") == 0 || "nodename nor servname provided, or not known") == 0 ||
strcmp(c->errstr,"no address associated with name") == 0)); strcmp(c->errstr, "No address associated with hostname") == 0 ||
strcmp(c->errstr, "Temporary failure in name resolution") == 0 ||
strcmp(c->errstr,
"hostname nor servname provided, or not known") == 0 ||
strcmp(c->errstr, "no address associated with name") == 0));
redisFree(c); redisFree(c);
} else {
printf("Skipping NXDOMAIN test. Found evil ISP!\n");
freeaddrinfo(ai_tmp);
}
test("Returns error when the port is not open: "); test("Returns error when the port is not open: ");
c = redisConnect((char*)"localhost", 1); c = redisConnect((char*)"localhost", 1);
...@@ -387,7 +479,7 @@ static void test_blocking_connection(struct config config) { ...@@ -387,7 +479,7 @@ static void test_blocking_connection(struct config config) {
redisContext *c; redisContext *c;
redisReply *reply; redisReply *reply;
c = connect(config); c = do_connect(config);
test("Is able to deliver commands: "); test("Is able to deliver commands: ");
reply = redisCommand(c,"PING"); reply = redisCommand(c,"PING");
...@@ -468,7 +560,7 @@ static void test_blocking_connection_timeouts(struct config config) { ...@@ -468,7 +560,7 @@ static void test_blocking_connection_timeouts(struct config config) {
const char *cmd = "DEBUG SLEEP 3\r\n"; const char *cmd = "DEBUG SLEEP 3\r\n";
struct timeval tv; struct timeval tv;
c = connect(config); c = do_connect(config);
test("Successfully completes a command when the timeout is not exceeded: "); test("Successfully completes a command when the timeout is not exceeded: ");
reply = redisCommand(c,"SET foo fast"); reply = redisCommand(c,"SET foo fast");
freeReplyObject(reply); freeReplyObject(reply);
...@@ -480,7 +572,7 @@ static void test_blocking_connection_timeouts(struct config config) { ...@@ -480,7 +572,7 @@ static void test_blocking_connection_timeouts(struct config config) {
freeReplyObject(reply); freeReplyObject(reply);
disconnect(c, 0); disconnect(c, 0);
c = connect(config); c = do_connect(config);
test("Does not return a reply when the command times out: "); test("Does not return a reply when the command times out: ");
s = write(c->fd, cmd, strlen(cmd)); s = write(c->fd, cmd, strlen(cmd));
tv.tv_sec = 0; tv.tv_sec = 0;
...@@ -514,7 +606,7 @@ static void test_blocking_io_errors(struct config config) { ...@@ -514,7 +606,7 @@ static void test_blocking_io_errors(struct config config) {
int major, minor; int major, minor;
/* Connect to target given by config. */ /* Connect to target given by config. */
c = connect(config); c = do_connect(config);
{ {
/* Find out Redis version to determine the path for the next test */ /* Find out Redis version to determine the path for the next test */
const char *field = "redis_version:"; const char *field = "redis_version:";
...@@ -549,7 +641,7 @@ static void test_blocking_io_errors(struct config config) { ...@@ -549,7 +641,7 @@ static void test_blocking_io_errors(struct config config) {
strcmp(c->errstr,"Server closed the connection") == 0); strcmp(c->errstr,"Server closed the connection") == 0);
redisFree(c); redisFree(c);
c = connect(config); c = do_connect(config);
test("Returns I/O error on socket timeout: "); test("Returns I/O error on socket timeout: ");
struct timeval tv = { 0, 1000 }; struct timeval tv = { 0, 1000 };
assert(redisSetTimeout(c,tv) == REDIS_OK); assert(redisSetTimeout(c,tv) == REDIS_OK);
...@@ -583,7 +675,7 @@ static void test_invalid_timeout_errors(struct config config) { ...@@ -583,7 +675,7 @@ static void test_invalid_timeout_errors(struct config config) {
} }
static void test_throughput(struct config config) { static void test_throughput(struct config config) {
redisContext *c = connect(config); redisContext *c = do_connect(config);
redisReply **replies; redisReply **replies;
int i, num; int i, num;
long long t1, t2; long long t1, t2;
...@@ -616,6 +708,17 @@ static void test_throughput(struct config config) { ...@@ -616,6 +708,17 @@ static void test_throughput(struct config config) {
free(replies); free(replies);
printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0); printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0);
replies = malloc(sizeof(redisReply*)*num);
t1 = usec();
for (i = 0; i < num; i++) {
replies[i] = redisCommand(c, "INCRBY incrkey %d", 1000000);
assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER);
}
t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies);
printf("\t(%dx INCRBY: %.3fs)\n", num, (t2-t1)/1000000.0);
num = 10000; num = 10000;
replies = malloc(sizeof(redisReply*)*num); replies = malloc(sizeof(redisReply*)*num);
for (i = 0; i < num; i++) for (i = 0; i < num; i++)
...@@ -644,6 +747,19 @@ static void test_throughput(struct config config) { ...@@ -644,6 +747,19 @@ static void test_throughput(struct config config) {
free(replies); free(replies);
printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
replies = malloc(sizeof(redisReply*)*num);
for (i = 0; i < num; i++)
redisAppendCommand(c,"INCRBY incrkey %d", 1000000);
t1 = usec();
for (i = 0; i < num; i++) {
assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK);
assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER);
}
t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies);
printf("\t(%dx INCRBY (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
disconnect(c, 0); disconnect(c, 0);
} }
......
...@@ -264,59 +264,75 @@ dir ./ ...@@ -264,59 +264,75 @@ dir ./
################################# REPLICATION ################################# ################################# REPLICATION #################################
# Master-Slave replication. Use slaveof to make a Redis instance a copy of # Master-Replica replication. Use replicaof to make a Redis instance a copy of
# another Redis server. A few things to understand ASAP about Redis replication. # another Redis server. A few things to understand ASAP about Redis replication.
# #
# +------------------+ +---------------+
# | Master | ---> | Replica |
# | (receive writes) | | (exact copy) |
# +------------------+ +---------------+
#
# 1) Redis replication is asynchronous, but you can configure a master to # 1) Redis replication is asynchronous, but you can configure a master to
# stop accepting writes if it appears to be not connected with at least # stop accepting writes if it appears to be not connected with at least
# a given number of slaves. # a given number of replicas.
# 2) Redis slaves are able to perform a partial resynchronization with the # 2) Redis replicas are able to perform a partial resynchronization with the
# master if the replication link is lost for a relatively small amount of # master if the replication link is lost for a relatively small amount of
# time. You may want to configure the replication backlog size (see the next # time. You may want to configure the replication backlog size (see the next
# sections of this file) with a sensible value depending on your needs. # sections of this file) with a sensible value depending on your needs.
# 3) Replication is automatic and does not need user intervention. After a # 3) Replication is automatic and does not need user intervention. After a
# network partition slaves automatically try to reconnect to masters # network partition replicas automatically try to reconnect to masters
# and resynchronize with them. # and resynchronize with them.
# #
# slaveof <masterip> <masterport> # replicaof <masterip> <masterport>
# If the master is password protected (using the "requirepass" configuration # If the master is password protected (using the "requirepass" configuration
# directive below) it is possible to tell the slave to authenticate before # directive below) it is possible to tell the replica to authenticate before
# starting the replication synchronization process, otherwise the master will # starting the replication synchronization process, otherwise the master will
# refuse the slave request. # refuse the replica request.
# #
# masterauth <master-password> # masterauth <master-password>
#
# However this is not enough if you are using Redis ACLs (for Redis version
# 6 or greater), and the default user is not capable of running the PSYNC
# command and/or other commands needed for replication. In this case it's
# better to configure a special user to use with replication, and specify the
# masteruser configuration as such:
#
# masteruser <username>
#
# When masteruser is specified, the replica will authenticate against its
# master using the new AUTH form: AUTH <username> <password>.
# When a slave loses its connection with the master, or when the replication # When a replica loses its connection with the master, or when the replication
# is still in progress, the slave can act in two different ways: # is still in progress, the replica can act in two different ways:
# #
# 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will # 1) if replica-serve-stale-data is set to 'yes' (the default) the replica will
# still reply to client requests, possibly with out of date data, or the # still reply to client requests, possibly with out of date data, or the
# data set may just be empty if this is the first synchronization. # data set may just be empty if this is the first synchronization.
# #
# 2) if slave-serve-stale-data is set to 'no' the slave will reply with # 2) if replica-serve-stale-data is set to 'no' the replica will reply with
# an error "SYNC with master in progress" to all the kind of commands # an error "SYNC with master in progress" to all the kind of commands
# but to INFO, SLAVEOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG, # but to INFO, replicaOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG,
# SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB, # SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB,
# COMMAND, POST, HOST: and LATENCY. # COMMAND, POST, HOST: and LATENCY.
# #
slave-serve-stale-data yes replica-serve-stale-data yes
# You can configure a slave instance to accept writes or not. Writing against # You can configure a replica instance to accept writes or not. Writing against
# a slave instance may be useful to store some ephemeral data (because data # a replica instance may be useful to store some ephemeral data (because data
# written on a slave will be easily deleted after resync with the master) but # written on a replica will be easily deleted after resync with the master) but
# may also cause problems if clients are writing to it because of a # may also cause problems if clients are writing to it because of a
# misconfiguration. # misconfiguration.
# #
# Since Redis 2.6 by default slaves are read-only. # Since Redis 2.6 by default replicas are read-only.
# #
# Note: read only slaves are not designed to be exposed to untrusted clients # Note: read only replicas are not designed to be exposed to untrusted clients
# on the internet. It's just a protection layer against misuse of the instance. # on the internet. It's just a protection layer against misuse of the instance.
# Still a read only slave exports by default all the administrative commands # Still a read only replica exports by default all the administrative commands
# such as CONFIG, DEBUG, and so forth. To a limited extent you can improve # such as CONFIG, DEBUG, and so forth. To a limited extent you can improve
# security of read only slaves using 'rename-command' to shadow all the # security of read only replicas using 'rename-command' to shadow all the
# administrative / dangerous commands. # administrative / dangerous commands.
slave-read-only yes replica-read-only yes
# Replication SYNC strategy: disk or socket. # Replication SYNC strategy: disk or socket.
# #
...@@ -324,25 +340,25 @@ slave-read-only yes ...@@ -324,25 +340,25 @@ slave-read-only yes
# WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY # WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY
# ------------------------------------------------------- # -------------------------------------------------------
# #
# New slaves and reconnecting slaves that are not able to continue the replication # New replicas and reconnecting replicas that are not able to continue the replication
# process just receiving differences, need to do what is called a "full # process just receiving differences, need to do what is called a "full
# synchronization". An RDB file is transmitted from the master to the slaves. # synchronization". An RDB file is transmitted from the master to the replicas.
# The transmission can happen in two different ways: # The transmission can happen in two different ways:
# #
# 1) Disk-backed: The Redis master creates a new process that writes the RDB # 1) Disk-backed: The Redis master creates a new process that writes the RDB
# file on disk. Later the file is transferred by the parent # file on disk. Later the file is transferred by the parent
# process to the slaves incrementally. # process to the replicas incrementally.
# 2) Diskless: The Redis master creates a new process that directly writes the # 2) Diskless: The Redis master creates a new process that directly writes the
# RDB file to slave sockets, without touching the disk at all. # RDB file to replica sockets, without touching the disk at all.
# #
# With disk-backed replication, while the RDB file is generated, more slaves # With disk-backed replication, while the RDB file is generated, more replicas
# can be queued and served with the RDB file as soon as the current child producing # can be queued and served with the RDB file as soon as the current child producing
# the RDB file finishes its work. With diskless replication instead once # the RDB file finishes its work. With diskless replication instead once
# the transfer starts, new slaves arriving will be queued and a new transfer # the transfer starts, new replicas arriving will be queued and a new transfer
# will start when the current one terminates. # will start when the current one terminates.
# #
# When diskless replication is used, the master waits a configurable amount of # When diskless replication is used, the master waits a configurable amount of
# time (in seconds) before starting the transfer in the hope that multiple slaves # time (in seconds) before starting the transfer in the hope that multiple replicas
# will arrive and the transfer can be parallelized. # will arrive and the transfer can be parallelized.
# #
# With slow disks and fast (large bandwidth) networks, diskless replication # With slow disks and fast (large bandwidth) networks, diskless replication
...@@ -351,157 +367,264 @@ repl-diskless-sync no ...@@ -351,157 +367,264 @@ repl-diskless-sync no
# When diskless replication is enabled, it is possible to configure the delay # When diskless replication is enabled, it is possible to configure the delay
# the server waits in order to spawn the child that transfers the RDB via socket # the server waits in order to spawn the child that transfers the RDB via socket
# to the slaves. # to the replicas.
# #
# This is important since once the transfer starts, it is not possible to serve # This is important since once the transfer starts, it is not possible to serve
# new slaves arriving, that will be queued for the next RDB transfer, so the server # new replicas arriving, that will be queued for the next RDB transfer, so the server
# waits a delay in order to let more slaves arrive. # waits a delay in order to let more replicas arrive.
# #
# The delay is specified in seconds, and by default is 5 seconds. To disable # The delay is specified in seconds, and by default is 5 seconds. To disable
# it entirely just set it to 0 seconds and the transfer will start ASAP. # it entirely just set it to 0 seconds and the transfer will start ASAP.
repl-diskless-sync-delay 5 repl-diskless-sync-delay 5
# Slaves send PINGs to server in a predefined interval. It's possible to change # Replicas send PINGs to server in a predefined interval. It's possible to change
# this interval with the repl_ping_slave_period option. The default value is 10 # this interval with the repl_ping_replica_period option. The default value is 10
# seconds. # seconds.
# #
# repl-ping-slave-period 10 # repl-ping-replica-period 10
# The following option sets the replication timeout for: # The following option sets the replication timeout for:
# #
# 1) Bulk transfer I/O during SYNC, from the point of view of slave. # 1) Bulk transfer I/O during SYNC, from the point of view of replica.
# 2) Master timeout from the point of view of slaves (data, pings). # 2) Master timeout from the point of view of replicas (data, pings).
# 3) Slave timeout from the point of view of masters (REPLCONF ACK pings). # 3) Replica timeout from the point of view of masters (REPLCONF ACK pings).
# #
# It is important to make sure that this value is greater than the value # It is important to make sure that this value is greater than the value
# specified for repl-ping-slave-period otherwise a timeout will be detected # specified for repl-ping-replica-period otherwise a timeout will be detected
# every time there is low traffic between the master and the slave. # every time there is low traffic between the master and the replica.
# #
# repl-timeout 60 # repl-timeout 60
# Disable TCP_NODELAY on the slave socket after SYNC? # Disable TCP_NODELAY on the replica socket after SYNC?
# #
# If you select "yes" Redis will use a smaller number of TCP packets and # If you select "yes" Redis will use a smaller number of TCP packets and
# less bandwidth to send data to slaves. But this can add a delay for # less bandwidth to send data to replicas. But this can add a delay for
# the data to appear on the slave side, up to 40 milliseconds with # the data to appear on the replica side, up to 40 milliseconds with
# Linux kernels using a default configuration. # Linux kernels using a default configuration.
# #
# If you select "no" the delay for data to appear on the slave side will # If you select "no" the delay for data to appear on the replica side will
# be reduced but more bandwidth will be used for replication. # be reduced but more bandwidth will be used for replication.
# #
# By default we optimize for low latency, but in very high traffic conditions # By default we optimize for low latency, but in very high traffic conditions
# or when the master and slaves are many hops away, turning this to "yes" may # or when the master and replicas are many hops away, turning this to "yes" may
# be a good idea. # be a good idea.
repl-disable-tcp-nodelay no repl-disable-tcp-nodelay no
# Set the replication backlog size. The backlog is a buffer that accumulates # Set the replication backlog size. The backlog is a buffer that accumulates
# slave data when slaves are disconnected for some time, so that when a slave # replica data when replicas are disconnected for some time, so that when a replica
# wants to reconnect again, often a full resync is not needed, but a partial # wants to reconnect again, often a full resync is not needed, but a partial
# resync is enough, just passing the portion of data the slave missed while # resync is enough, just passing the portion of data the replica missed while
# disconnected. # disconnected.
# #
# The bigger the replication backlog, the longer the time the slave can be # The bigger the replication backlog, the longer the time the replica can be
# disconnected and later be able to perform a partial resynchronization. # disconnected and later be able to perform a partial resynchronization.
# #
# The backlog is only allocated once there is at least a slave connected. # The backlog is only allocated once there is at least a replica connected.
# #
# repl-backlog-size 1mb # repl-backlog-size 1mb
# After a master has no longer connected slaves for some time, the backlog # After a master has no longer connected replicas for some time, the backlog
# will be freed. The following option configures the amount of seconds that # will be freed. The following option configures the amount of seconds that
# need to elapse, starting from the time the last slave disconnected, for # need to elapse, starting from the time the last replica disconnected, for
# the backlog buffer to be freed. # the backlog buffer to be freed.
# #
# Note that slaves never free the backlog for timeout, since they may be # Note that replicas never free the backlog for timeout, since they may be
# promoted to masters later, and should be able to correctly "partially # promoted to masters later, and should be able to correctly "partially
# resynchronize" with the slaves: hence they should always accumulate backlog. # resynchronize" with the replicas: hence they should always accumulate backlog.
# #
# A value of 0 means to never release the backlog. # A value of 0 means to never release the backlog.
# #
# repl-backlog-ttl 3600 # repl-backlog-ttl 3600
# The slave priority is an integer number published by Redis in the INFO output. # The replica priority is an integer number published by Redis in the INFO output.
# It is used by Redis Sentinel in order to select a slave to promote into a # It is used by Redis Sentinel in order to select a replica to promote into a
# master if the master is no longer working correctly. # master if the master is no longer working correctly.
# #
# A slave with a low priority number is considered better for promotion, so # A replica with a low priority number is considered better for promotion, so
# for instance if there are three slaves with priority 10, 100, 25 Sentinel will # for instance if there are three replicas with priority 10, 100, 25 Sentinel will
# pick the one with priority 10, that is the lowest. # pick the one with priority 10, that is the lowest.
# #
# However a special priority of 0 marks the slave as not able to perform the # However a special priority of 0 marks the replica as not able to perform the
# role of master, so a slave with priority of 0 will never be selected by # role of master, so a replica with priority of 0 will never be selected by
# Redis Sentinel for promotion. # Redis Sentinel for promotion.
# #
# By default the priority is 100. # By default the priority is 100.
slave-priority 100 replica-priority 100
# It is possible for a master to stop accepting writes if there are less than # It is possible for a master to stop accepting writes if there are less than
# N slaves connected, having a lag less or equal than M seconds. # N replicas connected, having a lag less or equal than M seconds.
# #
# The N slaves need to be in "online" state. # The N replicas need to be in "online" state.
# #
# The lag in seconds, that must be <= the specified value, is calculated from # The lag in seconds, that must be <= the specified value, is calculated from
# the last ping received from the slave, that is usually sent every second. # the last ping received from the replica, that is usually sent every second.
# #
# This option does not GUARANTEE that N replicas will accept the write, but # This option does not GUARANTEE that N replicas will accept the write, but
# will limit the window of exposure for lost writes in case not enough slaves # will limit the window of exposure for lost writes in case not enough replicas
# are available, to the specified number of seconds. # are available, to the specified number of seconds.
# #
# For example to require at least 3 slaves with a lag <= 10 seconds use: # For example to require at least 3 replicas with a lag <= 10 seconds use:
# #
# min-slaves-to-write 3 # min-replicas-to-write 3
# min-slaves-max-lag 10 # min-replicas-max-lag 10
# #
# Setting one or the other to 0 disables the feature. # Setting one or the other to 0 disables the feature.
# #
# By default min-slaves-to-write is set to 0 (feature disabled) and # By default min-replicas-to-write is set to 0 (feature disabled) and
# min-slaves-max-lag is set to 10. # min-replicas-max-lag is set to 10.
# A Redis master is able to list the address and port of the attached # A Redis master is able to list the address and port of the attached
# slaves in different ways. For example the "INFO replication" section # replicas in different ways. For example the "INFO replication" section
# offers this information, which is used, among other tools, by # offers this information, which is used, among other tools, by
# Redis Sentinel in order to discover slave instances. # Redis Sentinel in order to discover replica instances.
# Another place where this info is available is in the output of the # Another place where this info is available is in the output of the
# "ROLE" command of a master. # "ROLE" command of a master.
# #
# The listed IP and address normally reported by a slave is obtained # The listed IP and address normally reported by a replica is obtained
# in the following way: # in the following way:
# #
# IP: The address is auto detected by checking the peer address # IP: The address is auto detected by checking the peer address
# of the socket used by the slave to connect with the master. # of the socket used by the replica to connect with the master.
# #
# Port: The port is communicated by the slave during the replication # Port: The port is communicated by the replica during the replication
# handshake, and is normally the port that the slave is using to # handshake, and is normally the port that the replica is using to
# list for connections. # listen for connections.
# #
# However when port forwarding or Network Address Translation (NAT) is # However when port forwarding or Network Address Translation (NAT) is
# used, the slave may be actually reachable via different IP and port # used, the replica may be actually reachable via different IP and port
# pairs. The following two options can be used by a slave in order to # pairs. The following two options can be used by a replica in order to
# report to its master a specific set of IP and port, so that both INFO # report to its master a specific set of IP and port, so that both INFO
# and ROLE will report those values. # and ROLE will report those values.
# #
# There is no need to use both the options if you need to override just # There is no need to use both the options if you need to override just
# the port or the IP address. # the port or the IP address.
# #
# slave-announce-ip 5.5.5.5 # replica-announce-ip 5.5.5.5
# slave-announce-port 1234 # replica-announce-port 1234
################################## SECURITY ################################### ################################## SECURITY ###################################
# Require clients to issue AUTH <PASSWORD> before processing any other
# commands. This might be useful in environments in which you do not trust
# others with access to the host running redis-server.
#
# This should stay commented out for backward compatibility and because most
# people do not need auth (e.g. they run their own servers).
#
# Warning: since Redis is pretty fast an outside user can try up to # Warning: since Redis is pretty fast an outside user can try up to
# 150k passwords per second against a good box. This means that you should # 1 million passwords per second against a modern box. This means that you
# use a very strong password otherwise it will be very easy to break. # should use very strong passwords, otherwise they will be very easy to break.
# Note that because the password is really a shared secret between the client
# and the server, and should not be memorized by any human, the password
# can be easily a long string from /dev/urandom or whatever, so by using a
# long and unguessable password no brute force attack will be possible.
# Redis ACL users are defined in the following format:
#
# user <username> ... acl rules ...
#
# For example:
#
# user worker +@list +@connection ~jobs:* on >ffa9203c493aa99
#
# The special username "default" is used for new connections. If this user
# has the "nopass" rule, then new connections will be immediately authenticated
# as the "default" user without the need of any password provided via the
# AUTH command. Otherwise if the "default" user is not flagged with "nopass"
# the connections will start in not authenticated state, and will require
# AUTH (or the HELLO command AUTH option) in order to be authenticated and
# start to work.
#
# The ACL rules that describe what an user can do are the following:
#
# on Enable the user: it is possible to authenticate as this user.
# off Disable the user: it's no longer possible to authenticate
# with this user, however the already authenticated connections
# will still work.
# +<command> Allow the execution of that command
# -<command> Disallow the execution of that command
# +@<category> Allow the execution of all the commands in such category
# with valid categories are like @admin, @set, @sortedset, ...
# and so forth, see the full list in the server.c file where
# the Redis command table is described and defined.
# The special category @all means all the commands, but currently
# present in the server, and that will be loaded in the future
# via modules.
# +<command>|subcommand Allow a specific subcommand of an otherwise
# disabled command. Note that this form is not
# allowed as negative like -DEBUG|SEGFAULT, but
# only additive starting with "+".
# allcommands Alias for +@all. Note that it implies the ability to execute
# all the future commands loaded via the modules system.
# nocommands Alias for -@all.
# ~<pattern> Add a pattern of keys that can be mentioned as part of
# commands. For instance ~* allows all the keys. The pattern
# is a glob-style pattern like the one of KEYS.
# It is possible to specify multiple patterns.
# allkeys Alias for ~*
# resetkeys Flush the list of allowed keys patterns.
# ><password> Add this passowrd to the list of valid password for the user.
# For example >mypass will add "mypass" to the list.
# This directive clears the "nopass" flag (see later).
# <<password> Remove this password from the list of valid passwords.
# nopass All the set passwords of the user are removed, and the user
# is flagged as requiring no password: it means that every
# password will work against this user. If this directive is
# used for the default user, every new connection will be
# immediately authenticated with the default user without
# any explicit AUTH command required. Note that the "resetpass"
# directive will clear this condition.
# resetpass Flush the list of allowed passwords. Moreover removes the
# "nopass" status. After "resetpass" the user has no associated
# passwords and there is no way to authenticate without adding
# some password (or setting it as "nopass" later).
# reset Performs the following actions: resetpass, resetkeys, off,
# -@all. The user returns to the same state it has immediately
# after its creation.
#
# ACL rules can be specified in any order: for instance you can start with
# passwords, then flags, or key patterns. However note that the additive
# and subtractive rules will CHANGE MEANING depending on the ordering.
# For instance see the following example:
#
# user alice on +@all -DEBUG ~* >somepassword
#
# This will allow "alice" to use all the commands with the exception of the
# DEBUG command, since +@all added all the commands to the set of the commands
# alice can use, and later DEBUG was removed. However if we invert the order
# of two ACL rules the result will be different:
#
# user alice on -DEBUG +@all ~* >somepassword
#
# Now DEBUG was removed when alice had yet no commands in the set of allowed
# commands, later all the commands are added, so the user will be able to
# execute everything.
#
# Basically ACL rules are processed left-to-right.
#
# For more information about ACL configuration please refer to
# the Redis web site at https://redis.io/topics/acl
# Using an external ACL file
#
# Instead of configuring users here in this file, it is possible to use
# a stand-alone file just listing users. The two methods cannot be mixed:
# if you configure users here and at the same time you activate the exteranl
# ACL file, the server will refuse to start.
#
# The format of the external ACL user file is exactly the same as the
# format that is used inside redis.conf to describe users.
#
# aclfile /etc/redis/users.acl
# IMPORTANT NOTE: starting with Redis 6 "requirepass" is just a compatiblity
# layer on top of the new ACL system. The option effect will be just setting
# the password for the default user. Clients will still authenticate using
# AUTH <password> as usually, or more explicitly with AUTH default <password>
# if they follow the new protocol: both will work.
# #
# requirepass foobared # requirepass foobared
# Command renaming. # Command renaming (DEPRECATED).
#
# ------------------------------------------------------------------------
# WARNING: avoid using this option if possible. Instead use ACLs to remove
# commands from the default user, and put them only in some admin user you
# create for administrative purposes.
# ------------------------------------------------------------------------
# #
# It is possible to change the name of dangerous commands in a shared # It is possible to change the name of dangerous commands in a shared
# environment. For instance the CONFIG command may be renamed into something # environment. For instance the CONFIG command may be renamed into something
...@@ -518,7 +641,7 @@ slave-priority 100 ...@@ -518,7 +641,7 @@ slave-priority 100
# rename-command CONFIG "" # rename-command CONFIG ""
# #
# Please note that changing the name of commands that are logged into the # Please note that changing the name of commands that are logged into the
# AOF file or transmitted to slaves may cause problems. # AOF file or transmitted to replicas may cause problems.
################################### CLIENTS #################################### ################################### CLIENTS ####################################
...@@ -547,15 +670,15 @@ slave-priority 100 ...@@ -547,15 +670,15 @@ slave-priority 100
# This option is usually useful when using Redis as an LRU or LFU cache, or to # This option is usually useful when using Redis as an LRU or LFU cache, or to
# set a hard memory limit for an instance (using the 'noeviction' policy). # set a hard memory limit for an instance (using the 'noeviction' policy).
# #
# WARNING: If you have slaves attached to an instance with maxmemory on, # WARNING: If you have replicas attached to an instance with maxmemory on,
# the size of the output buffers needed to feed the slaves are subtracted # the size of the output buffers needed to feed the replicas are subtracted
# from the used memory count, so that network problems / resyncs will # from the used memory count, so that network problems / resyncs will
# not trigger a loop where keys are evicted, and in turn the output # not trigger a loop where keys are evicted, and in turn the output
# buffer of slaves is full with DELs of keys evicted triggering the deletion # buffer of replicas is full with DELs of keys evicted triggering the deletion
# of more keys, and so forth until the database is completely emptied. # of more keys, and so forth until the database is completely emptied.
# #
# In short... if you have slaves attached it is suggested that you set a lower # In short... if you have replicas attached it is suggested that you set a lower
# limit for maxmemory so that there is some free RAM on the system for slave # limit for maxmemory so that there is some free RAM on the system for replica
# output buffers (but this is not needed if the policy is 'noeviction'). # output buffers (but this is not needed if the policy is 'noeviction').
# #
# maxmemory <bytes> # maxmemory <bytes>
...@@ -602,6 +725,26 @@ slave-priority 100 ...@@ -602,6 +725,26 @@ slave-priority 100
# #
# maxmemory-samples 5 # maxmemory-samples 5
# Starting from Redis 5, by default a replica will ignore its maxmemory setting
# (unless it is promoted to master after a failover or manually). It means
# that the eviction of keys will be just handled by the master, sending the
# DEL commands to the replica as keys evict in the master side.
#
# This behavior ensures that masters and replicas stay consistent, and is usually
# what you want, however if your replica is writable, or you want the replica to have
# a different memory setting, and you are sure all the writes performed to the
# replica are idempotent, then you may change this default (but be sure to understand
# what you are doing).
#
# Note that since the replica by default does not evict, it may end using more
# memory than the one set via maxmemory (there are certain buffers that may
# be larger on the replica, or data structures may sometimes take more memory and so
# forth). So make sure you monitor your replicas and make sure they have enough
# memory to never hit a real out-of-memory condition before the master hits
# the configured maxmemory setting.
#
# replica-ignore-maxmemory yes
############################# LAZY FREEING #################################### ############################# LAZY FREEING ####################################
# Redis has two primitives to delete keys. One is called DEL and is a blocking # Redis has two primitives to delete keys. One is called DEL and is a blocking
...@@ -637,7 +780,7 @@ slave-priority 100 ...@@ -637,7 +780,7 @@ slave-priority 100
# or SORT with STORE option may delete existing keys. The SET command # or SORT with STORE option may delete existing keys. The SET command
# itself removes any old content of the specified key in order to replace # itself removes any old content of the specified key in order to replace
# it with the specified string. # it with the specified string.
# 4) During replication, when a slave performs a full resynchronization with # 4) During replication, when a replica performs a full resynchronization with
# its master, the content of the whole database is removed in order to # its master, the content of the whole database is removed in order to
# load the RDB file just transferred. # load the RDB file just transferred.
# #
...@@ -649,7 +792,7 @@ slave-priority 100 ...@@ -649,7 +792,7 @@ slave-priority 100
lazyfree-lazy-eviction no lazyfree-lazy-eviction no
lazyfree-lazy-expire no lazyfree-lazy-expire no
lazyfree-lazy-server-del no lazyfree-lazy-server-del no
slave-lazy-flush no replica-lazy-flush no
############################## APPEND ONLY MODE ############################### ############################## APPEND ONLY MODE ###############################
...@@ -826,42 +969,42 @@ lua-time-limit 5000 ...@@ -826,42 +969,42 @@ lua-time-limit 5000
# #
# cluster-node-timeout 15000 # cluster-node-timeout 15000
# A slave of a failing master will avoid to start a failover if its data # A replica of a failing master will avoid to start a failover if its data
# looks too old. # looks too old.
# #
# There is no simple way for a slave to actually have an exact measure of # There is no simple way for a replica to actually have an exact measure of
# its "data age", so the following two checks are performed: # its "data age", so the following two checks are performed:
# #
# 1) If there are multiple slaves able to failover, they exchange messages # 1) If there are multiple replicas able to failover, they exchange messages
# in order to try to give an advantage to the slave with the best # in order to try to give an advantage to the replica with the best
# replication offset (more data from the master processed). # replication offset (more data from the master processed).
# Slaves will try to get their rank by offset, and apply to the start # Replicas will try to get their rank by offset, and apply to the start
# of the failover a delay proportional to their rank. # of the failover a delay proportional to their rank.
# #
# 2) Every single slave computes the time of the last interaction with # 2) Every single replica computes the time of the last interaction with
# its master. This can be the last ping or command received (if the master # its master. This can be the last ping or command received (if the master
# is still in the "connected" state), or the time that elapsed since the # is still in the "connected" state), or the time that elapsed since the
# disconnection with the master (if the replication link is currently down). # disconnection with the master (if the replication link is currently down).
# If the last interaction is too old, the slave will not try to failover # If the last interaction is too old, the replica will not try to failover
# at all. # at all.
# #
# The point "2" can be tuned by user. Specifically a slave will not perform # The point "2" can be tuned by user. Specifically a replica will not perform
# the failover if, since the last interaction with the master, the time # the failover if, since the last interaction with the master, the time
# elapsed is greater than: # elapsed is greater than:
# #
# (node-timeout * slave-validity-factor) + repl-ping-slave-period # (node-timeout * replica-validity-factor) + repl-ping-replica-period
# #
# So for example if node-timeout is 30 seconds, and the slave-validity-factor # So for example if node-timeout is 30 seconds, and the replica-validity-factor
# is 10, and assuming a default repl-ping-slave-period of 10 seconds, the # is 10, and assuming a default repl-ping-replica-period of 10 seconds, the
# slave will not try to failover if it was not able to talk with the master # replica will not try to failover if it was not able to talk with the master
# for longer than 310 seconds. # for longer than 310 seconds.
# #
# A large slave-validity-factor may allow slaves with too old data to failover # A large replica-validity-factor may allow replicas with too old data to failover
# a master, while a too small value may prevent the cluster from being able to # a master, while a too small value may prevent the cluster from being able to
# elect a slave at all. # elect a replica at all.
# #
# For maximum availability, it is possible to set the slave-validity-factor # For maximum availability, it is possible to set the replica-validity-factor
# to a value of 0, which means, that slaves will always try to failover the # to a value of 0, which means, that replicas will always try to failover the
# master regardless of the last time they interacted with the master. # master regardless of the last time they interacted with the master.
# (However they'll always try to apply a delay proportional to their # (However they'll always try to apply a delay proportional to their
# offset rank). # offset rank).
...@@ -869,22 +1012,22 @@ lua-time-limit 5000 ...@@ -869,22 +1012,22 @@ lua-time-limit 5000
# Zero is the only value able to guarantee that when all the partitions heal # Zero is the only value able to guarantee that when all the partitions heal
# the cluster will always be able to continue. # the cluster will always be able to continue.
# #
# cluster-slave-validity-factor 10 # cluster-replica-validity-factor 10
# Cluster slaves are able to migrate to orphaned masters, that are masters # Cluster replicas are able to migrate to orphaned masters, that are masters
# that are left without working slaves. This improves the cluster ability # that are left without working replicas. This improves the cluster ability
# to resist to failures as otherwise an orphaned master can't be failed over # to resist to failures as otherwise an orphaned master can't be failed over
# in case of failure if it has no working slaves. # in case of failure if it has no working replicas.
# #
# Slaves migrate to orphaned masters only if there are still at least a # Replicas migrate to orphaned masters only if there are still at least a
# given number of other working slaves for their old master. This number # given number of other working replicas for their old master. This number
# is the "migration barrier". A migration barrier of 1 means that a slave # is the "migration barrier". A migration barrier of 1 means that a replica
# will migrate only if there is at least 1 other working slave for its master # will migrate only if there is at least 1 other working replica for its master
# and so forth. It usually reflects the number of slaves you want for every # and so forth. It usually reflects the number of replicas you want for every
# master in your cluster. # master in your cluster.
# #
# Default is 1 (slaves migrate only if their masters remain with at least # Default is 1 (replicas migrate only if their masters remain with at least
# one slave). To disable migration just set it to a very large value. # one replica). To disable migration just set it to a very large value.
# A value of 0 can be set but is useful only for debugging and dangerous # A value of 0 can be set but is useful only for debugging and dangerous
# in production. # in production.
# #
...@@ -903,7 +1046,7 @@ lua-time-limit 5000 ...@@ -903,7 +1046,7 @@ lua-time-limit 5000
# #
# cluster-require-full-coverage yes # cluster-require-full-coverage yes
# This option, when set to yes, prevents slaves from trying to failover its # This option, when set to yes, prevents replicas from trying to failover its
# master during master failures. However the master can still perform a # master during master failures. However the master can still perform a
# manual failover, if forced to do so. # manual failover, if forced to do so.
# #
...@@ -911,7 +1054,7 @@ lua-time-limit 5000 ...@@ -911,7 +1054,7 @@ lua-time-limit 5000
# data center operations, where we want one side to never be promoted if not # data center operations, where we want one side to never be promoted if not
# in the case of a total DC failure. # in the case of a total DC failure.
# #
# cluster-slave-no-failover no # cluster-replica-no-failover no
# In order to setup your cluster make sure to read the documentation # In order to setup your cluster make sure to read the documentation
# available at http://redis.io web site. # available at http://redis.io web site.
...@@ -1040,6 +1183,61 @@ latency-monitor-threshold 0 ...@@ -1040,6 +1183,61 @@ latency-monitor-threshold 0
# specify at least one of K or E, no events will be delivered. # specify at least one of K or E, no events will be delivered.
notify-keyspace-events "" notify-keyspace-events ""
############################### GOPHER SERVER #################################
# Redis contains an implementation of the Gopher protocol, as specified in
# the RFC 1436 (https://www.ietf.org/rfc/rfc1436.txt).
#
# The Gopher protocol was very popular in the late '90s. It is an alternative
# to the web, and the implementation both server and client side is so simple
# that the Redis server has just 100 lines of code in order to implement this
# support.
#
# What do you do with Gopher nowadays? Well Gopher never *really* died, and
# lately there is a movement in order for the Gopher more hierarchical content
# composed of just plain text documents to be resurrected. Some want a simpler
# internet, others believe that the mainstream internet became too much
# controlled, and it's cool to create an alternative space for people that
# want a bit of fresh air.
#
# Anyway for the 10nth birthday of the Redis, we gave it the Gopher protocol
# as a gift.
#
# --- HOW IT WORKS? ---
#
# The Redis Gopher support uses the inline protocol of Redis, and specifically
# two kind of inline requests that were anyway illegal: an empty request
# or any request that starts with "/" (there are no Redis commands starting
# with such a slash). Normal RESP2/RESP3 requests are completely out of the
# path of the Gopher protocol implementation and are served as usually as well.
#
# If you open a connection to Redis when Gopher is enabled and send it
# a string like "/foo", if there is a key named "/foo" it is served via the
# Gopher protocol.
#
# In order to create a real Gopher "hole" (the name of a Gopher site in Gopher
# talking), you likely need a script like the following:
#
# https://github.com/antirez/gopher2redis
#
# --- SECURITY WARNING ---
#
# If you plan to put Redis on the internet in a publicly accessible address
# to server Gopher pages MAKE SURE TO SET A PASSWORD to the instance.
# Once a password is set:
#
# 1. The Gopher server (when enabled, not by default) will kill serve
# content via Gopher.
# 2. However other commands cannot be called before the client will
# authenticate.
#
# So use the 'requirepass' option to protect your instance.
#
# To enable Gopher support uncomment the following line and set
# the option from no (the default) to yes.
#
# gopher-enabled no
############################### ADVANCED CONFIG ############################### ############################### ADVANCED CONFIG ###############################
# Hashes are encoded using a memory efficient data structure when they have a # Hashes are encoded using a memory efficient data structure when they have a
...@@ -1145,7 +1343,7 @@ activerehashing yes ...@@ -1145,7 +1343,7 @@ activerehashing yes
# The limit can be set differently for the three different classes of clients: # The limit can be set differently for the three different classes of clients:
# #
# normal -> normal clients including MONITOR clients # normal -> normal clients including MONITOR clients
# slave -> slave clients # replica -> replica clients
# pubsub -> clients subscribed to at least one pubsub channel or pattern # pubsub -> clients subscribed to at least one pubsub channel or pattern
# #
# The syntax of every client-output-buffer-limit directive is the following: # The syntax of every client-output-buffer-limit directive is the following:
...@@ -1166,12 +1364,12 @@ activerehashing yes ...@@ -1166,12 +1364,12 @@ activerehashing yes
# asynchronous clients may create a scenario where data is requested faster # asynchronous clients may create a scenario where data is requested faster
# than it can read. # than it can read.
# #
# Instead there is a default limit for pubsub and slave clients, since # Instead there is a default limit for pubsub and replica clients, since
# subscribers and slaves receive data in a push fashion. # subscribers and replicas receive data in a push fashion.
# #
# Both the hard or the soft limit can be disabled by setting them to zero. # Both the hard or the soft limit can be disabled by setting them to zero.
client-output-buffer-limit normal 0 0 0 client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60 client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60 client-output-buffer-limit pubsub 32mb 8mb 60
# Client query buffers accumulate new commands. They are limited to a fixed # Client query buffers accumulate new commands. They are limited to a fixed
...@@ -1205,6 +1403,22 @@ client-output-buffer-limit pubsub 32mb 8mb 60 ...@@ -1205,6 +1403,22 @@ client-output-buffer-limit pubsub 32mb 8mb 60
# 100 only in environments where very low latency is required. # 100 only in environments where very low latency is required.
hz 10 hz 10
# Normally it is useful to have an HZ value which is proportional to the
# number of clients connected. This is useful in order, for instance, to
# avoid too many clients are processed for each background task invocation
# in order to avoid latency spikes.
#
# Since the default HZ value by default is conservatively set to 10, Redis
# offers, and enables by default, the ability to use an adaptive HZ value
# which will temporary raise when there are many connected clients.
#
# When dynamic HZ is enabled, the actual configured HZ will be used as
# as a baseline, but multiples of the configured HZ value will be actually
# used as needed once more clients are connected. In this way an idle
# instance will use very little CPU time while a busy instance will be
# more responsive.
dynamic-hz yes
# When a child rewrites the AOF file, if the following option is enabled # When a child rewrites the AOF file, if the following option is enabled
# the file will be fsync-ed every 32 MB of data generated. This is useful # the file will be fsync-ed every 32 MB of data generated. This is useful
# in order to commit the file to the disk more incrementally and avoid # in order to commit the file to the disk more incrementally and avoid
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment