Commit f5ca1f9e authored by Oran Agra's avatar Oran Agra
Browse files

Merge unstable into 6.2

parents 92bde124 61d3fdb4
...@@ -17,7 +17,7 @@ jobs: ...@@ -17,7 +17,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: make - name: make
run: make run: make REDIS_CFLAGS='-Werror -DREDIS_TEST'
- name: test - name: test
run: | run: |
sudo apt-get install tcl8.6 sudo apt-get install tcl8.6
...@@ -28,6 +28,8 @@ jobs: ...@@ -28,6 +28,8 @@ jobs:
run: ./runtest-sentinel run: ./runtest-sentinel
- name: cluster tests - name: cluster tests
run: ./runtest-cluster run: ./runtest-cluster
- name: unittest
run: ./src/redis-server test all
test-ubuntu-libc-malloc: test-ubuntu-libc-malloc:
runs-on: ubuntu-latest runs-on: ubuntu-latest
...@@ -76,7 +78,7 @@ jobs: ...@@ -76,7 +78,7 @@ jobs:
- name: make - name: make
run: | run: |
sudo apt-get update && sudo apt-get install libc6-dev-i386 sudo apt-get update && sudo apt-get install libc6-dev-i386
make 32bit make 32bit REDIS_CFLAGS='-Werror -DREDIS_TEST'
- name: test - name: test
run: | run: |
sudo apt-get install tcl8.6 sudo apt-get install tcl8.6
...@@ -89,6 +91,8 @@ jobs: ...@@ -89,6 +91,8 @@ jobs:
run: ./runtest-sentinel run: ./runtest-sentinel
- name: cluster tests - name: cluster tests
run: ./runtest-cluster run: ./runtest-cluster
- name: unittest
run: ./src/redis-server test all
test-ubuntu-tls: test-ubuntu-tls:
runs-on: ubuntu-latest runs-on: ubuntu-latest
...@@ -142,7 +146,7 @@ jobs: ...@@ -142,7 +146,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: make - name: make
run: make valgrind run: make valgrind REDIS_CFLAGS='-Werror -DREDIS_TEST'
- name: test - name: test
run: | run: |
sudo apt-get update sudo apt-get update
...@@ -150,6 +154,10 @@ jobs: ...@@ -150,6 +154,10 @@ jobs:
./runtest --valgrind --verbose --clients 1 --dump-logs ./runtest --valgrind --verbose --clients 1 --dump-logs
- name: module api test - name: module api test
run: ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1 run: ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1
- name: unittest
run: |
valgrind --track-origins=yes --suppressions=./src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full --log-file=err.txt ./src/redis-server test all
if grep -q 0x err.txt; then cat err.txt; exit 1; fi
test-valgrind-no-malloc-usable-size: test-valgrind-no-malloc-usable-size:
runs-on: ubuntu-latest runs-on: ubuntu-latest
...@@ -259,6 +267,7 @@ jobs: ...@@ -259,6 +267,7 @@ jobs:
test-alpine-jemalloc: test-alpine-jemalloc:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.repository == 'redis/redis'
container: alpine:latest container: alpine:latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
...@@ -279,6 +288,7 @@ jobs: ...@@ -279,6 +288,7 @@ jobs:
test-alpine-libc-malloc: test-alpine-libc-malloc:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.repository == 'redis/redis'
container: alpine:latest container: alpine:latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
......
...@@ -15,10 +15,10 @@ Another good example is to think of Redis as a more complex version of memcached ...@@ -15,10 +15,10 @@ Another good example is to think of Redis as a more complex version of memcached
If you want to know more, this is a list of selected starting points: If you want to know more, this is a list of selected starting points:
* Introduction to Redis data types. http://redis.io/topics/data-types-intro * Introduction to Redis data types. https://redis.io/topics/data-types-intro
* Try Redis directly inside your browser. http://try.redis.io * Try Redis directly inside your browser. http://try.redis.io
* The full list of Redis commands. http://redis.io/commands * The full list of Redis commands. https://redis.io/commands
* There is much more inside the official Redis documentation. http://redis.io/documentation * There is much more inside the official Redis documentation. https://redis.io/documentation
Building Redis Building Redis
-------------- --------------
...@@ -49,7 +49,7 @@ To append a suffix to Redis program names, use: ...@@ -49,7 +49,7 @@ To append a suffix to Redis program names, use:
% make PROG_SUFFIX="-alt" % make PROG_SUFFIX="-alt"
You can run a 32 bit Redis binary using: You can build a 32 bit Redis binary using:
% make 32bit % make 32bit
...@@ -184,7 +184,7 @@ then in another terminal try the following: ...@@ -184,7 +184,7 @@ then in another terminal try the following:
(integer) 2 (integer) 2
redis> redis>
You can find the list of all the available commands at http://redis.io/commands. You can find the list of all the available commands at https://redis.io/commands.
Installing Redis Installing Redis
----------------- -----------------
...@@ -294,8 +294,8 @@ the structure definition. ...@@ -294,8 +294,8 @@ the structure definition.
Another important Redis data structure is the one defining a client. Another important Redis data structure is the one defining a client.
In the past it was called `redisClient`, now just `client`. The structure In the past it was called `redisClient`, now just `client`. The structure
has many fields, here we'll just show the main ones: has many fields, here we'll just show the main ones:
```c
struct client { struct client {
int fd; int fd;
sds querybuf; sds querybuf;
int argc; int argc;
...@@ -304,9 +304,9 @@ has many fields, here we'll just show the main ones: ...@@ -304,9 +304,9 @@ has many fields, here we'll just show the main ones:
int flags; int flags;
list *reply; list *reply;
char buf[PROTO_REPLY_CHUNK_BYTES]; char buf[PROTO_REPLY_CHUNK_BYTES];
... many other fields ... // ... many other fields ...
} }
```
The client structure defines a *connected client*: The client structure defines a *connected client*:
* The `fd` field is the client socket file descriptor. * The `fd` field is the client socket file descriptor.
...@@ -453,7 +453,7 @@ Other C files ...@@ -453,7 +453,7 @@ Other C files
* `scripting.c` implements Lua scripting. It is completely self-contained and isolated from the rest of the Redis implementation and is simple enough to understand if you are familiar with the Lua API. * `scripting.c` implements Lua scripting. It is completely self-contained and isolated from the rest of the Redis implementation and is simple enough to understand if you are familiar with the Lua API.
* `cluster.c` implements the Redis Cluster. Probably a good read only after being very familiar with the rest of the Redis code base. If you want to read `cluster.c` make sure to read the [Redis Cluster specification][3]. * `cluster.c` implements the Redis Cluster. Probably a good read only after being very familiar with the rest of the Redis code base. If you want to read `cluster.c` make sure to read the [Redis Cluster specification][3].
[3]: http://redis.io/topics/cluster-spec [3]: https://redis.io/topics/cluster-spec
Anatomy of a Redis command Anatomy of a Redis command
--- ---
......
...@@ -150,6 +150,11 @@ tcp-keepalive 300 ...@@ -150,6 +150,11 @@ tcp-keepalive 300
# #
# tls-cert-file redis.crt # tls-cert-file redis.crt
# tls-key-file redis.key # tls-key-file redis.key
#
# If the key file is encrypted using a passphrase, it can be included here
# as well.
#
# tls-key-file-pass secret
# Normally Redis uses the same certificate for both server functions (accepting # Normally Redis uses the same certificate for both server functions (accepting
# connections) and client functions (replicating from a master, establishing # connections) and client functions (replicating from a master, establishing
...@@ -162,6 +167,11 @@ tcp-keepalive 300 ...@@ -162,6 +167,11 @@ tcp-keepalive 300
# #
# tls-client-cert-file client.crt # tls-client-cert-file client.crt
# tls-client-key-file client.key # tls-client-key-file client.key
#
# If the key file is encrypted using a passphrase, it can be included here
# as well.
#
# tls-client-key-file-pass secret
# Configure a DH parameters file to enable Diffie-Hellman (DH) key exchange: # Configure a DH parameters file to enable Diffie-Hellman (DH) key exchange:
# #
...@@ -657,6 +667,18 @@ repl-disable-tcp-nodelay no ...@@ -657,6 +667,18 @@ repl-disable-tcp-nodelay no
# By default the priority is 100. # By default the priority is 100.
replica-priority 100 replica-priority 100
# -----------------------------------------------------------------------------
# By default, Redis Sentinel includes all replicas in its reports. A replica
# can be excluded from Redis Sentinel's announcements. An unannounced replica
# will be ignored by the 'sentinel replicas <master>' command and won't be
# exposed to Redis Sentinel's clients.
#
# This option does not change the behavior of replica-priority. Even with
# replica-announced set to 'no', the replica can be promoted to master. To
# prevent this behavior, set replica-priority to 0.
#
# replica-announced yes
# 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 replicas connected, having a lag less or equal than M seconds. # N replicas connected, having a lag less or equal than M seconds.
# #
...@@ -895,7 +917,7 @@ acllog-max-len 128 ...@@ -895,7 +917,7 @@ acllog-max-len 128
# order to provide better out-of-the-box Pub/Sub security. Therefore, it is # order to provide better out-of-the-box Pub/Sub security. Therefore, it is
# recommended that you explicitly define Pub/Sub permissions for all users # recommended that you explicitly define Pub/Sub permissions for all users
# rather then rely on implicit default values. Once you've set explicit # rather then rely on implicit default values. Once you've set explicit
# Pub/Sub for all exisitn users, you should uncomment the following line. # Pub/Sub for all existing users, you should uncomment the following line.
# #
# acl-pubsub-default resetchannels # acl-pubsub-default resetchannels
...@@ -1225,7 +1247,7 @@ disable-thp yes ...@@ -1225,7 +1247,7 @@ disable-thp yes
# If the AOF is enabled on startup Redis will load the AOF, that is the file # If the AOF is enabled on startup Redis will load the AOF, that is the file
# with the better durability guarantees. # with the better durability guarantees.
# #
# Please check http://redis.io/topics/persistence for more information. # Please check https://redis.io/topics/persistence for more information.
appendonly no appendonly no
...@@ -1434,12 +1456,21 @@ lua-time-limit 5000 ...@@ -1434,12 +1456,21 @@ lua-time-limit 5000
# master in your cluster. # master in your cluster.
# #
# Default is 1 (replicas migrate only if their masters remain with at least # Default is 1 (replicas migrate only if their masters remain with at least
# one replica). To disable migration just set it to a very large value. # one replica). To disable migration just set it to a very large value or
# set cluster-allow-replica-migration to 'no'.
# 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.
# #
# cluster-migration-barrier 1 # cluster-migration-barrier 1
# Turning off this option allows to use less automatic cluster configuration.
# It both disables migration to orphaned masters and migration from masters
# that became empty.
#
# Default is 'yes' (allow automatic migrations).
#
# cluster-allow-replica-migration yes
# By default Redis Cluster nodes stop accepting queries if they detect there # By default Redis Cluster nodes stop accepting queries if they detect there
# is at least a hash slot uncovered (no available node is serving it). # is at least a hash slot uncovered (no available node is serving it).
# This way if the cluster is partially down (for example a range of hash slots # This way if the cluster is partially down (for example a range of hash slots
...@@ -1480,7 +1511,7 @@ lua-time-limit 5000 ...@@ -1480,7 +1511,7 @@ lua-time-limit 5000
# cluster-allow-reads-when-down no # cluster-allow-reads-when-down 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 https://redis.io web site.
########################## CLUSTER DOCKER/NAT support ######################## ########################## CLUSTER DOCKER/NAT support ########################
...@@ -1490,16 +1521,21 @@ lua-time-limit 5000 ...@@ -1490,16 +1521,21 @@ lua-time-limit 5000
# #
# In order to make Redis Cluster working in such environments, a static # In order to make Redis Cluster working in such environments, a static
# configuration where each node knows its public address is needed. The # configuration where each node knows its public address is needed. The
# following two options are used for this scope, and are: # following four options are used for this scope, and are:
# #
# * cluster-announce-ip # * cluster-announce-ip
# * cluster-announce-port # * cluster-announce-port
# * cluster-announce-tls-port
# * cluster-announce-bus-port # * cluster-announce-bus-port
# #
# Each instructs the node about its address, client port, and cluster message # Each instructs the node about its address, client ports (for connections
# bus port. The information is then published in the header of the bus packets # without and with TLS) and cluster message bus port. The information is then
# so that other nodes will be able to correctly map the address of the node # published in the header of the bus packets so that other nodes will be able to
# publishing the information. # correctly map the address of the node publishing the information.
#
# If cluster-tls is set to yes and cluster-announce-tls-port is omitted or set
# to zero, then cluster-announce-port refers to the TLS port. Note also that
# cluster-announce-tls-port has no effect if cluster-tls is set to no.
# #
# If the above options are not used, the normal Redis Cluster auto-detection # If the above options are not used, the normal Redis Cluster auto-detection
# will be used instead. # will be used instead.
...@@ -1512,7 +1548,8 @@ lua-time-limit 5000 ...@@ -1512,7 +1548,8 @@ lua-time-limit 5000
# Example: # Example:
# #
# cluster-announce-ip 10.1.1.5 # cluster-announce-ip 10.1.1.5
# cluster-announce-port 6379 # cluster-announce-tls-port 6379
# cluster-announce-port 0
# cluster-announce-bus-port 6380 # cluster-announce-bus-port 6380
################################## SLOW LOG ################################### ################################## SLOW LOG ###################################
...@@ -1563,7 +1600,7 @@ latency-monitor-threshold 0 ...@@ -1563,7 +1600,7 @@ latency-monitor-threshold 0
############################# EVENT NOTIFICATION ############################## ############################# EVENT NOTIFICATION ##############################
# Redis can notify Pub/Sub clients about events happening in the key space. # Redis can notify Pub/Sub clients about events happening in the key space.
# This feature is documented at http://redis.io/topics/notifications # This feature is documented at https://redis.io/topics/notifications
# #
# For instance if keyspace events notification is enabled, and a client # For instance if keyspace events notification is enabled, and a client
# performs a DEL operation on key "foo" stored in the Database 0, two # performs a DEL operation on key "foo" stored in the Database 0, two
...@@ -1586,8 +1623,9 @@ latency-monitor-threshold 0 ...@@ -1586,8 +1623,9 @@ latency-monitor-threshold 0
# x Expired events (events generated every time a key expires) # x Expired events (events generated every time a key expires)
# e Evicted events (events generated when a key is evicted for maxmemory) # e Evicted events (events generated when a key is evicted for maxmemory)
# t Stream commands # t Stream commands
# d Module key type events
# m Key-miss events (Note: It is not included in the 'A' class) # m Key-miss events (Note: It is not included in the 'A' class)
# A Alias for g$lshzxet, so that the "AKE" string means all the events # A Alias for g$lshzxetd, so that the "AKE" string means all the events
# (Except key-miss events which are excluded from 'A' due to their # (Except key-miss events which are excluded from 'A' due to their
# unique nature). # unique nature).
# #
......
...@@ -256,6 +256,17 @@ endif ...@@ -256,6 +256,17 @@ endif
FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a $(LIBSSL_LIBS) $(LIBCRYPTO_LIBS) FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a $(LIBSSL_LIBS) $(LIBCRYPTO_LIBS)
endif endif
ifndef V
define MAKE_INSTALL
@printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$(1)$(ENDCOLOR) 1>&2
@$(INSTALL) $(1) $(2)
endef
else
define MAKE_INSTALL
$(INSTALL) $(1) $(2)
endef
endif
REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS) REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS)
REDIS_LD=$(QUIET_LINK)$(CC) $(FINAL_LDFLAGS) REDIS_LD=$(QUIET_LINK)$(CC) $(FINAL_LDFLAGS)
REDIS_INSTALL=$(QUIET_INSTALL)$(INSTALL) REDIS_INSTALL=$(QUIET_INSTALL)$(INSTALL)
...@@ -351,9 +362,6 @@ $(REDIS_CLI_NAME): $(REDIS_CLI_OBJ) ...@@ -351,9 +362,6 @@ $(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)
$(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ) $(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ)
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/hdr_histogram/hdr_histogram.o $(FINAL_LIBS) $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/hdr_histogram/hdr_histogram.o $(FINAL_LIBS)
dict-benchmark: dict.c zmalloc.c sds.c siphash.c mt19937-64.c
$(REDIS_CC) $(FINAL_CFLAGS) $^ -D DICT_BENCHMARK_MAIN -o $@ $(FINAL_LIBS)
DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ:%.o=%.d) DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ:%.o=%.d)
-include $(DEP) -include $(DEP)
...@@ -364,7 +372,7 @@ DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ ...@@ -364,7 +372,7 @@ DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ
$(REDIS_CC) -MMD -o $@ -c $< $(REDIS_CC) -MMD -o $@ -c $<
clean: clean:
rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html Makefile.dep dict-benchmark rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html Makefile.dep
rm -f $(DEP) rm -f $(DEP)
.PHONY: clean .PHONY: clean
...@@ -417,9 +425,9 @@ src/help.h: ...@@ -417,9 +425,9 @@ src/help.h:
install: all install: all
@mkdir -p $(INSTALL_BIN) @mkdir -p $(INSTALL_BIN)
$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(INSTALL_BIN) $(call MAKE_INSTALL,$(REDIS_SERVER_NAME),$(INSTALL_BIN))
$(REDIS_INSTALL) $(REDIS_BENCHMARK_NAME) $(INSTALL_BIN) $(call MAKE_INSTALL,$(REDIS_BENCHMARK_NAME),$(INSTALL_BIN))
$(REDIS_INSTALL) $(REDIS_CLI_NAME) $(INSTALL_BIN) $(call MAKE_INSTALL,$(REDIS_CLI_NAME),$(INSTALL_BIN))
@ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_CHECK_RDB_NAME) @ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_CHECK_RDB_NAME)
@ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_CHECK_AOF_NAME) @ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_CHECK_AOF_NAME)
@ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_SENTINEL_NAME) @ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_SENTINEL_NAME)
......
...@@ -245,7 +245,7 @@ user *ACLCreateUser(const char *name, size_t namelen) { ...@@ -245,7 +245,7 @@ user *ACLCreateUser(const char *name, size_t namelen) {
if (raxFind(Users,(unsigned char*)name,namelen) != raxNotFound) return NULL; if (raxFind(Users,(unsigned char*)name,namelen) != raxNotFound) return NULL;
user *u = zmalloc(sizeof(*u)); user *u = zmalloc(sizeof(*u));
u->name = sdsnewlen(name,namelen); u->name = sdsnewlen(name,namelen);
u->flags = USER_FLAG_DISABLED | server.acl_pubusub_default; u->flags = USER_FLAG_DISABLED | server.acl_pubsub_default;
u->allowed_subcommands = NULL; u->allowed_subcommands = NULL;
u->passwords = listCreate(); u->passwords = listCreate();
u->patterns = listCreate(); u->patterns = listCreate();
...@@ -652,6 +652,7 @@ sds ACLDescribeUser(user *u) { ...@@ -652,6 +652,7 @@ sds ACLDescribeUser(user *u) {
if (u->flags & USER_FLAG_ALLCHANNELS) { if (u->flags & USER_FLAG_ALLCHANNELS) {
res = sdscatlen(res,"&* ",3); res = sdscatlen(res,"&* ",3);
} else { } else {
res = sdscatlen(res,"resetchannels ",14);
listRewind(u->channels,&li); listRewind(u->channels,&li);
while((ln = listNext(&li))) { while((ln = listNext(&li))) {
sds thispat = listNodeValue(ln); sds thispat = listNodeValue(ln);
...@@ -1000,6 +1001,8 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) { ...@@ -1000,6 +1001,8 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) {
serverAssert(ACLSetUser(u,"resetpass",-1) == C_OK); serverAssert(ACLSetUser(u,"resetpass",-1) == C_OK);
serverAssert(ACLSetUser(u,"resetkeys",-1) == C_OK); serverAssert(ACLSetUser(u,"resetkeys",-1) == C_OK);
serverAssert(ACLSetUser(u,"resetchannels",-1) == C_OK); serverAssert(ACLSetUser(u,"resetchannels",-1) == C_OK);
if (server.acl_pubsub_default & USER_FLAG_ALLCHANNELS)
serverAssert(ACLSetUser(u,"allchannels",-1) == C_OK);
serverAssert(ACLSetUser(u,"off",-1) == C_OK); serverAssert(ACLSetUser(u,"off",-1) == C_OK);
serverAssert(ACLSetUser(u,"sanitize-payload",-1) == C_OK); serverAssert(ACLSetUser(u,"sanitize-payload",-1) == C_OK);
serverAssert(ACLSetUser(u,"-@all",-1) == C_OK); serverAssert(ACLSetUser(u,"-@all",-1) == C_OK);
...@@ -1180,9 +1183,9 @@ int ACLCheckCommandPerm(client *c, int *keyidxptr) { ...@@ -1180,9 +1183,9 @@ int ACLCheckCommandPerm(client *c, int *keyidxptr) {
/* If there is no associated user, the connection can run anything. */ /* If there is no associated user, the connection can run anything. */
if (u == NULL) return ACL_OK; if (u == NULL) return ACL_OK;
/* Check if the user can execute this command. */ /* Check if the user can execute this command or if the command
if (!(u->flags & USER_FLAG_ALLCOMMANDS) && * doesn't need to be authenticated (hello, auth). */
c->cmd->proc != authCommand) if (!(u->flags & USER_FLAG_ALLCOMMANDS) && !(c->cmd->flags & CMD_NO_AUTH))
{ {
/* If the bit is not set we have to check further, in case the /* If the bit is not set we have to check further, in case the
* command is allowed just with that specific subcommand. */ * command is allowed just with that specific subcommand. */
...@@ -1360,6 +1363,22 @@ int ACLCheckPubsubPerm(client *c, int idx, int count, int literal, int *idxptr) ...@@ -1360,6 +1363,22 @@ int ACLCheckPubsubPerm(client *c, int idx, int count, int literal, int *idxptr)
} }
/* Check whether the command is ready to be exceuted by ACLCheckCommandPerm.
* If check passes, then check whether pub/sub channels of the command is
* ready to be executed by ACLCheckPubsubPerm */
int ACLCheckAllPerm(client *c, int *idxptr) {
int acl_retval = ACLCheckCommandPerm(c,idxptr);
if (acl_retval != ACL_OK)
return acl_retval;
if (c->cmd->proc == publishCommand)
acl_retval = ACLCheckPubsubPerm(c,1,1,0,idxptr);
else if (c->cmd->proc == subscribeCommand)
acl_retval = ACLCheckPubsubPerm(c,1,c->argc-1,0,idxptr);
else if (c->cmd->proc == psubscribeCommand)
acl_retval = ACLCheckPubsubPerm(c,1,c->argc-1,1,idxptr);
return acl_retval;
}
/* ============================================================================= /* =============================================================================
* ACL loading / saving functions * ACL loading / saving functions
* ==========================================================================*/ * ==========================================================================*/
...@@ -1873,6 +1892,10 @@ void addACLLogEntry(client *c, int reason, int argpos, sds username) { ...@@ -1873,6 +1892,10 @@ void addACLLogEntry(client *c, int reason, int argpos, sds username) {
void aclCommand(client *c) { void aclCommand(client *c) {
char *sub = c->argv[1]->ptr; char *sub = c->argv[1]->ptr;
if (!strcasecmp(sub,"setuser") && c->argc >= 3) { if (!strcasecmp(sub,"setuser") && c->argc >= 3) {
/* Consider information about passwords or permissions
* to be sensitive, which will be the arguments for this
* subcommand. */
preventCommandLogging(c);
sds username = c->argv[2]->ptr; sds username = c->argv[2]->ptr;
/* Check username validity. */ /* Check username validity. */
if (ACLStringHasSpaces(username,sdslen(username))) { if (ACLStringHasSpaces(username,sdslen(username))) {
......
...@@ -239,7 +239,7 @@ int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id) ...@@ -239,7 +239,7 @@ int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)
return AE_ERR; /* NO event with the specified ID found */ return AE_ERR; /* NO event with the specified ID found */
} }
/* How many milliseconds until the first timer should fire. /* How many microseconds until the first timer should fire.
* If there are no timers, -1 is returned. * If there are no timers, -1 is returned.
* *
* Note that's O(N) since time events are unsorted. * Note that's O(N) since time events are unsorted.
...@@ -248,7 +248,7 @@ int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id) ...@@ -248,7 +248,7 @@ int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)
* Much better but still insertion or deletion of timers is O(N). * Much better but still insertion or deletion of timers is O(N).
* 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)). * 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)).
*/ */
static long msUntilEarliestTimer(aeEventLoop *eventLoop) { static int64_t usUntilEarliestTimer(aeEventLoop *eventLoop) {
aeTimeEvent *te = eventLoop->timeEventHead; aeTimeEvent *te = eventLoop->timeEventHead;
if (te == NULL) return -1; if (te == NULL) return -1;
...@@ -260,8 +260,7 @@ static long msUntilEarliestTimer(aeEventLoop *eventLoop) { ...@@ -260,8 +260,7 @@ static long msUntilEarliestTimer(aeEventLoop *eventLoop) {
} }
monotime now = getMonotonicUs(); monotime now = getMonotonicUs();
return (now >= earliest->when) return (now >= earliest->when) ? 0 : earliest->when - now;
? 0 : (long)((earliest->when - now) / 1000);
} }
/* Process time events */ /* Process time events */
...@@ -361,14 +360,14 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags) ...@@ -361,14 +360,14 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) { ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
int j; int j;
struct timeval tv, *tvp; struct timeval tv, *tvp;
long msUntilTimer = -1; int64_t usUntilTimer = -1;
if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT)) if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
msUntilTimer = msUntilEarliestTimer(eventLoop); usUntilTimer = usUntilEarliestTimer(eventLoop);
if (msUntilTimer >= 0) { if (usUntilTimer >= 0) {
tv.tv_sec = msUntilTimer / 1000; tv.tv_sec = usUntilTimer / 1000000;
tv.tv_usec = (msUntilTimer % 1000) * 1000; tv.tv_usec = usUntilTimer % 1000000;
tvp = &tv; tvp = &tv;
} else { } else {
/* If we have to check for events but need to return /* If we have to check for events but need to return
......
...@@ -111,7 +111,7 @@ static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { ...@@ -111,7 +111,7 @@ static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
int retval, numevents = 0; int retval, numevents = 0;
retval = epoll_wait(state->epfd,state->events,eventLoop->setsize, retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,
tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1); tvp ? (tvp->tv_sec*1000 + (tvp->tv_usec + 999)/1000) : -1);
if (retval > 0) { if (retval > 0) {
int j; int j;
......
...@@ -186,27 +186,6 @@ int anetDisableTcpNoDelay(char *err, int fd) ...@@ -186,27 +186,6 @@ int anetDisableTcpNoDelay(char *err, int fd)
return anetSetTcpNoDelay(err, fd, 0); return anetSetTcpNoDelay(err, fd, 0);
} }
int anetSetSendBuffer(char *err, int fd, int buffsize)
{
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1)
{
anetSetError(err, "setsockopt SO_SNDBUF: %s", strerror(errno));
return ANET_ERR;
}
return ANET_OK;
}
int anetTcpKeepAlive(char *err, int fd)
{
int yes = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) == -1) {
anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno));
return ANET_ERR;
}
return ANET_OK;
}
/* Set the socket send timeout (SO_SNDTIMEO socket option) to the specified /* Set the socket send timeout (SO_SNDTIMEO socket option) to the specified
* number of milliseconds, or disable it if the 'ms' argument is zero. */ * number of milliseconds, or disable it if the 'ms' argument is zero. */
int anetSendTimeout(char *err, int fd, long long ms) { int anetSendTimeout(char *err, int fd, long long ms) {
...@@ -378,23 +357,11 @@ end: ...@@ -378,23 +357,11 @@ end:
} }
} }
int anetTcpConnect(char *err, const char *addr, int port)
{
return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONE);
}
int anetTcpNonBlockConnect(char *err, const char *addr, int port) int anetTcpNonBlockConnect(char *err, const char *addr, int port)
{ {
return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONBLOCK); return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONBLOCK);
} }
int anetTcpNonBlockBindConnect(char *err, const char *addr, int port,
const char *source_addr)
{
return anetTcpGenericConnect(err,addr,port,source_addr,
ANET_CONNECT_NONBLOCK);
}
int anetTcpNonBlockBestEffortBindConnect(char *err, const char *addr, int port, int anetTcpNonBlockBestEffortBindConnect(char *err, const char *addr, int port,
const char *source_addr) const char *source_addr)
{ {
...@@ -430,46 +397,6 @@ int anetUnixGenericConnect(char *err, const char *path, int flags) ...@@ -430,46 +397,6 @@ int anetUnixGenericConnect(char *err, const char *path, int flags)
return s; return s;
} }
int anetUnixConnect(char *err, const char *path)
{
return anetUnixGenericConnect(err,path,ANET_CONNECT_NONE);
}
int anetUnixNonBlockConnect(char *err, const char *path)
{
return anetUnixGenericConnect(err,path,ANET_CONNECT_NONBLOCK);
}
/* Like read(2) but make sure 'count' is read before to return
* (unless error or EOF condition is encountered) */
int anetRead(int fd, char *buf, int count)
{
ssize_t nread, totlen = 0;
while(totlen != count) {
nread = read(fd,buf,count-totlen);
if (nread == 0) return totlen;
if (nread == -1) return -1;
totlen += nread;
buf += nread;
}
return totlen;
}
/* Like write(2) but make sure 'count' is written before to return
* (unless error is encountered) */
int anetWrite(int fd, char *buf, int count)
{
ssize_t nwritten, totlen = 0;
while(totlen != count) {
nwritten = write(fd,buf,count-totlen);
if (nwritten == 0) return totlen;
if (nwritten == -1) return -1;
totlen += nwritten;
buf += nwritten;
}
return totlen;
}
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog) { static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog) {
if (bind(s,sa,len) == -1) { if (bind(s,sa,len) == -1) {
anetSetError(err, "bind: %s", strerror(errno)); anetSetError(err, "bind: %s", strerror(errno));
......
...@@ -53,26 +53,19 @@ ...@@ -53,26 +53,19 @@
#define FD_TO_PEER_NAME 0 #define FD_TO_PEER_NAME 0
#define FD_TO_SOCK_NAME 1 #define FD_TO_SOCK_NAME 1
int anetTcpConnect(char *err, const char *addr, int port);
int anetTcpNonBlockConnect(char *err, const char *addr, int port); int anetTcpNonBlockConnect(char *err, const char *addr, int port);
int anetTcpNonBlockBindConnect(char *err, const char *addr, int port, const char *source_addr);
int anetTcpNonBlockBestEffortBindConnect(char *err, const char *addr, int port, const char *source_addr); int anetTcpNonBlockBestEffortBindConnect(char *err, const char *addr, int port, const char *source_addr);
int anetUnixConnect(char *err, const char *path);
int anetUnixNonBlockConnect(char *err, const char *path);
int anetRead(int fd, char *buf, int count);
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len, int flags); int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len, int flags);
int anetTcpServer(char *err, int port, char *bindaddr, int backlog); int anetTcpServer(char *err, int port, char *bindaddr, int backlog);
int anetTcp6Server(char *err, int port, char *bindaddr, int backlog); int anetTcp6Server(char *err, int port, char *bindaddr, int backlog);
int anetUnixServer(char *err, char *path, mode_t perm, int backlog); int anetUnixServer(char *err, char *path, mode_t perm, int backlog);
int anetTcpAccept(char *err, int serversock, char *ip, size_t ip_len, int *port); int anetTcpAccept(char *err, int serversock, char *ip, size_t ip_len, int *port);
int anetUnixAccept(char *err, int serversock); int anetUnixAccept(char *err, int serversock);
int anetWrite(int fd, char *buf, int count);
int anetNonBlock(char *err, int fd); int anetNonBlock(char *err, int fd);
int anetBlock(char *err, int fd); int anetBlock(char *err, int fd);
int anetCloexec(int fd); int anetCloexec(int fd);
int anetEnableTcpNoDelay(char *err, int fd); int anetEnableTcpNoDelay(char *err, int fd);
int anetDisableTcpNoDelay(char *err, int fd); int anetDisableTcpNoDelay(char *err, int fd);
int anetTcpKeepAlive(char *err, int fd);
int anetSendTimeout(char *err, int fd, long long ms); int anetSendTimeout(char *err, int fd, long long ms);
int anetRecvTimeout(char *err, int fd, long long ms); int anetRecvTimeout(char *err, int fd, long long ms);
int anetFdToString(int fd, char *ip, size_t ip_len, int *port, int fd_to_str_type); int anetFdToString(int fd, char *ip, size_t ip_len, int *port, int fd_to_str_type);
......
...@@ -218,7 +218,7 @@ void killAppendOnlyChild(void) { ...@@ -218,7 +218,7 @@ void killAppendOnlyChild(void) {
serverLog(LL_NOTICE,"Killing running AOF rewrite child: %ld", serverLog(LL_NOTICE,"Killing running AOF rewrite child: %ld",
(long) server.child_pid); (long) server.child_pid);
if (kill(server.child_pid,SIGUSR1) != -1) { if (kill(server.child_pid,SIGUSR1) != -1) {
while(wait3(&statloc,0,NULL) != server.child_pid); while(waitpid(-1, &statloc, 0) != server.child_pid);
} }
/* Reset the buffer accumulating changes while the child saves. */ /* Reset the buffer accumulating changes while the child saves. */
aofRewriteBufferReset(); aofRewriteBufferReset();
...@@ -234,9 +234,12 @@ void killAppendOnlyChild(void) { ...@@ -234,9 +234,12 @@ void killAppendOnlyChild(void) {
void stopAppendOnly(void) { void stopAppendOnly(void) {
serverAssert(server.aof_state != AOF_OFF); serverAssert(server.aof_state != AOF_OFF);
flushAppendOnlyFile(1); flushAppendOnlyFile(1);
redis_fsync(server.aof_fd); if (redis_fsync(server.aof_fd) == -1) {
serverLog(LL_WARNING,"Fail to fsync the AOF file: %s",strerror(errno));
} else {
server.aof_fsync_offset = server.aof_current_size; server.aof_fsync_offset = server.aof_current_size;
server.aof_last_fsync = server.unixtime; server.aof_last_fsync = server.unixtime;
}
close(server.aof_fd); close(server.aof_fd);
server.aof_fd = -1; server.aof_fd = -1;
...@@ -290,6 +293,15 @@ int startAppendOnly(void) { ...@@ -290,6 +293,15 @@ int startAppendOnly(void) {
server.aof_last_fsync = server.unixtime; server.aof_last_fsync = server.unixtime;
server.aof_fd = newfd; server.aof_fd = newfd;
/* If AOF fsync error in bio job, we just ignore it and log the event. */
int aof_bio_fsync_status;
atomicGet(server.aof_bio_fsync_status, aof_bio_fsync_status);
if (aof_bio_fsync_status == C_ERR) {
serverLog(LL_WARNING,
"AOF reopen, just ignore the AOF fsync error in bio job");
atomicSet(server.aof_bio_fsync_status,C_OK);
}
/* If AOF was in error state, we just ignore it and log the event. */ /* If AOF was in error state, we just ignore it and log the event. */
if (server.aof_last_write_status == C_ERR) { if (server.aof_last_write_status == C_ERR) {
serverLog(LL_WARNING,"AOF reopen, just ignore the last error."); serverLog(LL_WARNING,"AOF reopen, just ignore the last error.");
...@@ -1590,7 +1602,7 @@ int rewriteAppendOnlyFile(char *filename) { ...@@ -1590,7 +1602,7 @@ int rewriteAppendOnlyFile(char *filename) {
if (write(server.aof_pipe_write_ack_to_parent,"!",1) != 1) goto werr; if (write(server.aof_pipe_write_ack_to_parent,"!",1) != 1) goto werr;
if (anetNonBlock(NULL,server.aof_pipe_read_ack_from_parent) != ANET_OK) if (anetNonBlock(NULL,server.aof_pipe_read_ack_from_parent) != ANET_OK)
goto werr; goto werr;
/* We read the ACK from the server using a 10 seconds timeout. Normally /* We read the ACK from the server using a 5 seconds timeout. Normally
* it should reply ASAP, but just in case we lose its reply, we are sure * it should reply ASAP, but just in case we lose its reply, we are sure
* the child will eventually get terminated. */ * the child will eventually get terminated. */
if (syncRead(server.aof_pipe_read_ack_from_parent,&byte,1,5000) != 1 || if (syncRead(server.aof_pipe_read_ack_from_parent,&byte,1,5000) != 1 ||
......
...@@ -37,7 +37,7 @@ const char *ascii_logo = ...@@ -37,7 +37,7 @@ const char *ascii_logo =
" | `-._ `._ / _.-' | PID: %ld\n" " | `-._ `._ / _.-' | PID: %ld\n"
" `-._ `-._ `-./ _.-' _.-' \n" " `-._ `-._ `-./ _.-' _.-' \n"
" |`-._`-._ `-.__.-' _.-'_.-'| \n" " |`-._`-._ `-.__.-' _.-'_.-'| \n"
" | `-._`-._ _.-'_.-' | http://redis.io \n" " | `-._`-._ _.-'_.-' | https://redis.io \n"
" `-._ `-._`-.__.-'_.-' _.-' \n" " `-._ `-._`-.__.-'_.-' _.-' \n"
" |`-._`-._ `-.__.-' _.-'_.-'| \n" " |`-._`-._ `-.__.-' _.-'_.-'| \n"
" | `-._`-._ _.-'_.-' | \n" " | `-._`-._ _.-'_.-' | \n"
......
...@@ -220,7 +220,23 @@ void *bioProcessBackgroundJobs(void *arg) { ...@@ -220,7 +220,23 @@ void *bioProcessBackgroundJobs(void *arg) {
if (type == BIO_CLOSE_FILE) { if (type == BIO_CLOSE_FILE) {
close(job->fd); close(job->fd);
} else if (type == BIO_AOF_FSYNC) { } else if (type == BIO_AOF_FSYNC) {
redis_fsync(job->fd); /* The fd may be closed by main thread and reused for another
* socket, pipe, or file. We just ignore these errno because
* aof fsync did not really fail. */
if (redis_fsync(job->fd) == -1 &&
errno != EBADF && errno != EINVAL)
{
int last_status;
atomicGet(server.aof_bio_fsync_status,last_status);
atomicSet(server.aof_bio_fsync_status,C_ERR);
atomicSet(server.aof_bio_fsync_errno,errno);
if (last_status == C_OK) {
serverLog(LL_WARNING,
"Fail to fsync the AOF file: %s",strerror(errno));
}
} else {
atomicSet(server.aof_bio_fsync_status,C_OK);
}
} else if (type == BIO_LAZY_FREE) { } else if (type == BIO_LAZY_FREE) {
job->free_fn(job->free_args); job->free_fn(job->free_args);
} else { } else {
......
...@@ -106,12 +106,11 @@ void blockClient(client *c, int btype) { ...@@ -106,12 +106,11 @@ void blockClient(client *c, int btype) {
void updateStatsOnUnblock(client *c, long blocked_us, long reply_us){ void updateStatsOnUnblock(client *c, long blocked_us, long reply_us){
const ustime_t total_cmd_duration = c->duration + blocked_us + reply_us; const ustime_t total_cmd_duration = c->duration + blocked_us + reply_us;
c->lastcmd->microseconds += total_cmd_duration; c->lastcmd->microseconds += total_cmd_duration;
/* Log the command into the Slow log if needed. */ /* Log the command into the Slow log if needed. */
if (!(c->lastcmd->flags & CMD_SKIP_SLOWLOG)) { slowlogPushCurrentCommand(c, c->lastcmd, total_cmd_duration);
slowlogPushEntryIfNeeded(c,c->argv,c->argc,total_cmd_duration);
/* Log the reply duration event. */ /* Log the reply duration event. */
latencyAddSampleIfNeeded("command-unblocking",reply_us/1000); latencyAddSampleIfNeeded("command-unblocking",reply_us/1000);
}
} }
/* This function is called in the beforeSleep() function of the event loop /* This function is called in the beforeSleep() function of the event loop
...@@ -188,6 +187,16 @@ void unblockClient(client *c) { ...@@ -188,6 +187,16 @@ void unblockClient(client *c) {
} else { } else {
serverPanic("Unknown btype in unblockClient()."); serverPanic("Unknown btype in unblockClient().");
} }
/* Reset the client for a new query since, for blocking commands
* we do not do it immediately after the command returns (when the
* client got blocked) in order to be still able to access the argument
* vector from module callbacks and updateStatsOnUnblock. */
if (c->btype != BLOCKED_PAUSE) {
freeClientOriginalArgv(c);
resetClient(c);
}
/* Clear the flags, and put the client in the unblocked list so that /* Clear the flags, and put the client in the unblocked list so that
* we'll process new commands in its query buffer ASAP. */ * we'll process new commands in its query buffer ASAP. */
server.blocked_clients--; server.blocked_clients--;
...@@ -279,7 +288,6 @@ void serveClientsBlockedOnListKey(robj *o, readyList *rl) { ...@@ -279,7 +288,6 @@ void serveClientsBlockedOnListKey(robj *o, readyList *rl) {
* freed by the next unblockClient() * freed by the next unblockClient()
* call. */ * call. */
if (dstkey) incrRefCount(dstkey); if (dstkey) incrRefCount(dstkey);
unblockClient(receiver);
monotime replyTimer; monotime replyTimer;
elapsedStart(&replyTimer); elapsedStart(&replyTimer);
...@@ -292,6 +300,7 @@ void serveClientsBlockedOnListKey(robj *o, readyList *rl) { ...@@ -292,6 +300,7 @@ void serveClientsBlockedOnListKey(robj *o, readyList *rl) {
listTypePush(o,value,wherefrom); listTypePush(o,value,wherefrom);
} }
updateStatsOnUnblock(receiver, 0, elapsedUs(replyTimer)); updateStatsOnUnblock(receiver, 0, elapsedUs(replyTimer));
unblockClient(receiver);
if (dstkey) decrRefCount(dstkey); if (dstkey) decrRefCount(dstkey);
decrRefCount(value); decrRefCount(value);
...@@ -335,11 +344,11 @@ void serveClientsBlockedOnSortedSetKey(robj *o, readyList *rl) { ...@@ -335,11 +344,11 @@ void serveClientsBlockedOnSortedSetKey(robj *o, readyList *rl) {
int where = (receiver->lastcmd && int where = (receiver->lastcmd &&
receiver->lastcmd->proc == bzpopminCommand) receiver->lastcmd->proc == bzpopminCommand)
? ZSET_MIN : ZSET_MAX; ? ZSET_MIN : ZSET_MAX;
unblockClient(receiver);
monotime replyTimer; monotime replyTimer;
elapsedStart(&replyTimer); elapsedStart(&replyTimer);
genericZpopCommand(receiver,&rl->key,1,where,1,NULL); genericZpopCommand(receiver,&rl->key,1,where,1,NULL);
updateStatsOnUnblock(receiver, 0, elapsedUs(replyTimer)); updateStatsOnUnblock(receiver, 0, elapsedUs(replyTimer));
unblockClient(receiver);
zcard--; zcard--;
/* Replicate the command. */ /* Replicate the command. */
...@@ -471,6 +480,10 @@ void serveClientsBlockedOnStreamKey(robj *o, readyList *rl) { ...@@ -471,6 +480,10 @@ void serveClientsBlockedOnStreamKey(robj *o, readyList *rl) {
void serveClientsBlockedOnKeyByModule(readyList *rl) { void serveClientsBlockedOnKeyByModule(readyList *rl) {
dictEntry *de; dictEntry *de;
/* Optimization: If no clients are in type BLOCKED_MODULE,
* we can skip this loop. */
if (!server.blocked_clients_by_type[BLOCKED_MODULE]) return;
/* We serve clients in the same order they blocked for /* We serve clients in the same order they blocked for
* this key, from the first blocked to the last. */ * this key, from the first blocked to the last. */
de = dictFind(rl->db->blocking_keys,rl->key); de = dictFind(rl->db->blocking_keys,rl->key);
...@@ -553,7 +566,7 @@ void handleClientsBlockedOnKeys(void) { ...@@ -553,7 +566,7 @@ void handleClientsBlockedOnKeys(void) {
* way we can lookup an object multiple times (BLMOVE does * way we can lookup an object multiple times (BLMOVE does
* that) without the risk of it being freed in the second * that) without the risk of it being freed in the second
* lookup, invalidating the first one. * lookup, invalidating the first one.
* See https://github.com/antirez/redis/pull/6554. */ * See https://github.com/redis/redis/pull/6554. */
server.fixed_time_expire++; server.fixed_time_expire++;
updateCachedTime(0); updateCachedTime(0);
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
typedef struct { typedef struct {
size_t keys; size_t keys;
size_t cow; size_t cow;
monotime cow_updated;
double progress; double progress;
childInfoType information_type; /* Type of information */ childInfoType information_type; /* Type of information */
} child_info_data; } child_info_data;
...@@ -69,18 +70,39 @@ void closeChildInfoPipe(void) { ...@@ -69,18 +70,39 @@ void closeChildInfoPipe(void) {
void sendChildInfoGeneric(childInfoType info_type, size_t keys, double progress, char *pname) { void sendChildInfoGeneric(childInfoType info_type, size_t keys, double progress, char *pname) {
if (server.child_info_pipe[1] == -1) return; if (server.child_info_pipe[1] == -1) return;
child_info_data data = {0}; /* zero everything, including padding to sattisfy valgrind */ static monotime cow_updated = 0;
data.information_type = info_type; static uint64_t cow_update_cost = 0;
data.keys = keys; static size_t cow = 0;
data.cow = zmalloc_get_private_dirty(-1);
data.progress = progress; child_info_data data = {0}; /* zero everything, including padding to satisfy valgrind */
/* When called to report current info, we need to throttle down CoW updates as they
* can be very expensive. To do that, we measure the time it takes to get a reading
* and schedule the next reading to happen not before time*CHILD_COW_COST_FACTOR
* passes. */
monotime now = getMonotonicUs();
if (info_type != CHILD_INFO_TYPE_CURRENT_INFO ||
!cow_updated ||
now - cow_updated > cow_update_cost * CHILD_COW_DUTY_CYCLE)
{
cow = zmalloc_get_private_dirty(-1);
cow_updated = getMonotonicUs();
cow_update_cost = cow_updated - now;
if (data.cow) { if (cow) {
serverLog((info_type == CHILD_INFO_TYPE_CURRENT_INFO) ? LL_VERBOSE : LL_NOTICE, serverLog((info_type == CHILD_INFO_TYPE_CURRENT_INFO) ? LL_VERBOSE : LL_NOTICE,
"%s: %zu MB of memory used by copy-on-write", "%s: %zu MB of memory used by copy-on-write",
pname, data.cow/(1024*1024)); pname, data.cow / (1024 * 1024));
}
} }
data.information_type = info_type;
data.keys = keys;
data.cow = cow;
data.cow_updated = cow_updated;
data.progress = progress;
ssize_t wlen = sizeof(data); ssize_t wlen = sizeof(data);
if (write(server.child_info_pipe[1], &data, wlen) != wlen) { if (write(server.child_info_pipe[1], &data, wlen) != wlen) {
...@@ -89,9 +111,10 @@ void sendChildInfoGeneric(childInfoType info_type, size_t keys, double progress, ...@@ -89,9 +111,10 @@ void sendChildInfoGeneric(childInfoType info_type, size_t keys, double progress,
} }
/* Update Child info. */ /* Update Child info. */
void updateChildInfo(childInfoType information_type, size_t cow, size_t keys, double progress) { void updateChildInfo(childInfoType information_type, size_t cow, monotime cow_updated, size_t keys, double progress) {
if (information_type == CHILD_INFO_TYPE_CURRENT_INFO) { if (information_type == CHILD_INFO_TYPE_CURRENT_INFO) {
server.stat_current_cow_bytes = cow; server.stat_current_cow_bytes = cow;
server.stat_current_cow_updated = cow_updated;
server.stat_current_save_keys_processed = keys; server.stat_current_save_keys_processed = keys;
if (progress != -1) server.stat_module_progress = progress; if (progress != -1) server.stat_module_progress = progress;
} else if (information_type == CHILD_INFO_TYPE_AOF_COW_SIZE) { } else if (information_type == CHILD_INFO_TYPE_AOF_COW_SIZE) {
...@@ -107,7 +130,7 @@ void updateChildInfo(childInfoType information_type, size_t cow, size_t keys, do ...@@ -107,7 +130,7 @@ void updateChildInfo(childInfoType information_type, size_t cow, size_t keys, do
* if complete data read into the buffer, * if complete data read into the buffer,
* data is stored into *buffer, and returns 1. * data is stored into *buffer, and returns 1.
* otherwise, the partial data is left in the buffer, waiting for the next read, and returns 0. */ * otherwise, the partial data is left in the buffer, waiting for the next read, and returns 0. */
int readChildInfo(childInfoType *information_type, size_t *cow, size_t *keys, double* progress) { int readChildInfo(childInfoType *information_type, size_t *cow, monotime *cow_updated, size_t *keys, double* progress) {
/* We are using here a static buffer in combination with the server.child_info_nread to handle short reads */ /* We are using here a static buffer in combination with the server.child_info_nread to handle short reads */
static child_info_data buffer; static child_info_data buffer;
ssize_t wlen = sizeof(buffer); ssize_t wlen = sizeof(buffer);
...@@ -124,6 +147,7 @@ int readChildInfo(childInfoType *information_type, size_t *cow, size_t *keys, do ...@@ -124,6 +147,7 @@ int readChildInfo(childInfoType *information_type, size_t *cow, size_t *keys, do
if (server.child_info_nread == wlen) { if (server.child_info_nread == wlen) {
*information_type = buffer.information_type; *information_type = buffer.information_type;
*cow = buffer.cow; *cow = buffer.cow;
*cow_updated = buffer.cow_updated;
*keys = buffer.keys; *keys = buffer.keys;
*progress = buffer.progress; *progress = buffer.progress;
return 1; return 1;
...@@ -137,12 +161,13 @@ void receiveChildInfo(void) { ...@@ -137,12 +161,13 @@ void receiveChildInfo(void) {
if (server.child_info_pipe[0] == -1) return; if (server.child_info_pipe[0] == -1) return;
size_t cow; size_t cow;
monotime cow_updated;
size_t keys; size_t keys;
double progress; double progress;
childInfoType information_type; childInfoType information_type;
/* Drain the pipe and update child info so that we get the final message. */ /* Drain the pipe and update child info so that we get the final message. */
while (readChildInfo(&information_type, &cow, &keys, &progress)) { while (readChildInfo(&information_type, &cow, &cow_updated, &keys, &progress)) {
updateChildInfo(information_type, cow, keys, progress); updateChildInfo(information_type, cow, cow_updated, keys, progress);
} }
} }
...@@ -55,7 +55,7 @@ void clusterSendFail(char *nodename); ...@@ -55,7 +55,7 @@ void clusterSendFail(char *nodename);
void clusterSendFailoverAuthIfNeeded(clusterNode *node, clusterMsg *request); void clusterSendFailoverAuthIfNeeded(clusterNode *node, clusterMsg *request);
void clusterUpdateState(void); void clusterUpdateState(void);
int clusterNodeGetSlotBit(clusterNode *n, int slot); int clusterNodeGetSlotBit(clusterNode *n, int slot);
sds clusterGenNodesDescription(int filter); sds clusterGenNodesDescription(int filter, int use_pport);
clusterNode *clusterLookupNode(const char *name); clusterNode *clusterLookupNode(const char *name);
int clusterNodeAddSlave(clusterNode *master, clusterNode *slave); int clusterNodeAddSlave(clusterNode *master, clusterNode *slave);
int clusterAddSlot(clusterNode *n, int slot); int clusterAddSlot(clusterNode *n, int slot);
...@@ -190,6 +190,9 @@ int clusterLoadConfig(char *filename) { ...@@ -190,6 +190,9 @@ int clusterLoadConfig(char *filename) {
* base port. */ * base port. */
n->cport = busp ? atoi(busp) : n->port + CLUSTER_PORT_INCR; n->cport = busp ? atoi(busp) : n->port + CLUSTER_PORT_INCR;
/* The plaintext port for client in a TLS cluster (n->pport) is not
* stored in nodes.conf. It is received later over the bus protocol. */
/* Parse flags */ /* Parse flags */
p = s = argv[2]; p = s = argv[2];
while(p) { while(p) {
...@@ -336,7 +339,7 @@ int clusterSaveConfig(int do_fsync) { ...@@ -336,7 +339,7 @@ int clusterSaveConfig(int do_fsync) {
/* Get the nodes description and concatenate our "vars" directive to /* Get the nodes description and concatenate our "vars" directive to
* save currentEpoch and lastVoteEpoch. */ * save currentEpoch and lastVoteEpoch. */
ci = clusterGenNodesDescription(CLUSTER_NODE_HANDSHAKE); ci = clusterGenNodesDescription(CLUSTER_NODE_HANDSHAKE, 0);
ci = sdscatprintf(ci,"vars currentEpoch %llu lastVoteEpoch %llu\n", ci = sdscatprintf(ci,"vars currentEpoch %llu lastVoteEpoch %llu\n",
(unsigned long long) server.cluster->currentEpoch, (unsigned long long) server.cluster->currentEpoch,
(unsigned long long) server.cluster->lastVoteEpoch); (unsigned long long) server.cluster->lastVoteEpoch);
...@@ -355,7 +358,7 @@ int clusterSaveConfig(int do_fsync) { ...@@ -355,7 +358,7 @@ int clusterSaveConfig(int do_fsync) {
if (write(fd,ci,sdslen(ci)) != (ssize_t)sdslen(ci)) goto err; if (write(fd,ci,sdslen(ci)) != (ssize_t)sdslen(ci)) goto err;
if (do_fsync) { if (do_fsync) {
server.cluster->todo_before_sleep &= ~CLUSTER_TODO_FSYNC_CONFIG; server.cluster->todo_before_sleep &= ~CLUSTER_TODO_FSYNC_CONFIG;
fsync(fd); if (fsync(fd) == -1) goto err;
} }
/* Truncate the file if needed to remove the final \n padding that /* Truncate the file if needed to remove the final \n padding that
...@@ -437,6 +440,26 @@ int clusterLockConfig(char *filename) { ...@@ -437,6 +440,26 @@ int clusterLockConfig(char *filename) {
return C_OK; return C_OK;
} }
/* Derives our ports to be announced in the cluster bus. */
void deriveAnnouncedPorts(int *announced_port, int *announced_pport,
int *announced_cport) {
int port = server.tls_cluster ? server.tls_port : server.port;
/* Default announced ports. */
*announced_port = port;
*announced_pport = server.tls_cluster ? server.port : 0;
*announced_cport = port + CLUSTER_PORT_INCR;
/* Config overriding announced ports. */
if (server.tls_cluster && server.cluster_announce_tls_port) {
*announced_port = server.cluster_announce_tls_port;
*announced_pport = server.cluster_announce_port;
} else if (server.cluster_announce_port) {
*announced_port = server.cluster_announce_port;
}
if (server.cluster_announce_bus_port) {
*announced_cport = server.cluster_announce_bus_port;
}
}
/* Some flags (currently just the NOFAILOVER flag) may need to be updated /* Some flags (currently just the NOFAILOVER flag) may need to be updated
* in the "myself" node based on the current configuration of the node, * in the "myself" node based on the current configuration of the node,
* that may change at runtime via CONFIG SET. This function changes the * that may change at runtime via CONFIG SET. This function changes the
...@@ -524,14 +547,9 @@ void clusterInit(void) { ...@@ -524,14 +547,9 @@ void clusterInit(void) {
memset(server.cluster->slots_keys_count,0, memset(server.cluster->slots_keys_count,0,
sizeof(server.cluster->slots_keys_count)); sizeof(server.cluster->slots_keys_count));
/* Set myself->port / cport to my listening ports, we'll just need to /* Set myself->port/cport/pport to my listening ports, we'll just need to
* discover the IP address via MEET messages. */ * discover the IP address via MEET messages. */
myself->port = port; deriveAnnouncedPorts(&myself->port, &myself->pport, &myself->cport);
myself->cport = port+CLUSTER_PORT_INCR;
if (server.cluster_announce_port)
myself->port = server.cluster_announce_port;
if (server.cluster_announce_bus_port)
myself->cport = server.cluster_announce_bus_port;
server.cluster->mf_end = 0; server.cluster->mf_end = 0;
resetManualFailover(); resetManualFailover();
...@@ -782,6 +800,7 @@ clusterNode *createClusterNode(char *nodename, int flags) { ...@@ -782,6 +800,7 @@ clusterNode *createClusterNode(char *nodename, int flags) {
memset(node->ip,0,sizeof(node->ip)); memset(node->ip,0,sizeof(node->ip));
node->port = 0; node->port = 0;
node->cport = 0; node->cport = 0;
node->pport = 0;
node->fail_reports = listCreate(); node->fail_reports = listCreate();
node->voted_time = 0; node->voted_time = 0;
node->orphaned_time = 0; node->orphaned_time = 0;
...@@ -1488,6 +1507,7 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) { ...@@ -1488,6 +1507,7 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {
if (node->link) freeClusterLink(node->link); if (node->link) freeClusterLink(node->link);
memcpy(node->ip,g->ip,NET_IP_STR_LEN); memcpy(node->ip,g->ip,NET_IP_STR_LEN);
node->port = ntohs(g->port); node->port = ntohs(g->port);
node->pport = ntohs(g->pport);
node->cport = ntohs(g->cport); node->cport = ntohs(g->cport);
node->flags &= ~CLUSTER_NODE_NOADDR; node->flags &= ~CLUSTER_NODE_NOADDR;
} }
...@@ -1509,6 +1529,7 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) { ...@@ -1509,6 +1529,7 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {
node = createClusterNode(g->nodename, flags); node = createClusterNode(g->nodename, flags);
memcpy(node->ip,g->ip,NET_IP_STR_LEN); memcpy(node->ip,g->ip,NET_IP_STR_LEN);
node->port = ntohs(g->port); node->port = ntohs(g->port);
node->pport = ntohs(g->pport);
node->cport = ntohs(g->cport); node->cport = ntohs(g->cport);
clusterAddNode(node); clusterAddNode(node);
} }
...@@ -1548,6 +1569,7 @@ int nodeUpdateAddressIfNeeded(clusterNode *node, clusterLink *link, ...@@ -1548,6 +1569,7 @@ int nodeUpdateAddressIfNeeded(clusterNode *node, clusterLink *link,
{ {
char ip[NET_IP_STR_LEN] = {0}; char ip[NET_IP_STR_LEN] = {0};
int port = ntohs(hdr->port); int port = ntohs(hdr->port);
int pport = ntohs(hdr->pport);
int cport = ntohs(hdr->cport); int cport = ntohs(hdr->cport);
/* We don't proceed if the link is the same as the sender link, as this /* We don't proceed if the link is the same as the sender link, as this
...@@ -1559,12 +1581,13 @@ int nodeUpdateAddressIfNeeded(clusterNode *node, clusterLink *link, ...@@ -1559,12 +1581,13 @@ int nodeUpdateAddressIfNeeded(clusterNode *node, clusterLink *link,
if (link == node->link) return 0; if (link == node->link) return 0;
nodeIp2String(ip,link,hdr->myip); nodeIp2String(ip,link,hdr->myip);
if (node->port == port && node->cport == cport && if (node->port == port && node->cport == cport && node->pport == pport &&
strcmp(ip,node->ip) == 0) return 0; strcmp(ip,node->ip) == 0) return 0;
/* IP / port is different, update it. */ /* IP / port is different, update it. */
memcpy(node->ip,ip,sizeof(ip)); memcpy(node->ip,ip,sizeof(ip));
node->port = port; node->port = port;
node->pport = pport;
node->cport = cport; node->cport = cport;
if (node->link) freeClusterLink(node->link); if (node->link) freeClusterLink(node->link);
node->flags &= ~CLUSTER_NODE_NOADDR; node->flags &= ~CLUSTER_NODE_NOADDR;
...@@ -1610,7 +1633,7 @@ void clusterSetNodeAsMaster(clusterNode *n) { ...@@ -1610,7 +1633,7 @@ void clusterSetNodeAsMaster(clusterNode *n) {
* case we receive the info via an UPDATE packet. */ * case we receive the info via an UPDATE packet. */
void clusterUpdateSlotsConfigWith(clusterNode *sender, uint64_t senderConfigEpoch, unsigned char *slots) { void clusterUpdateSlotsConfigWith(clusterNode *sender, uint64_t senderConfigEpoch, unsigned char *slots) {
int j; int j;
clusterNode *curmaster, *newmaster = NULL; clusterNode *curmaster = NULL, *newmaster = NULL;
/* The dirty slots list is a list of slots for which we lose the ownership /* The dirty slots list is a list of slots for which we lose the ownership
* while having still keys inside. This usually happens after a failover * while having still keys inside. This usually happens after a failover
* or after a manual cluster reconfiguration operated by the admin. * or after a manual cluster reconfiguration operated by the admin.
...@@ -1621,6 +1644,12 @@ void clusterUpdateSlotsConfigWith(clusterNode *sender, uint64_t senderConfigEpoc ...@@ -1621,6 +1644,12 @@ void clusterUpdateSlotsConfigWith(clusterNode *sender, uint64_t senderConfigEpoc
uint16_t dirty_slots[CLUSTER_SLOTS]; uint16_t dirty_slots[CLUSTER_SLOTS];
int dirty_slots_count = 0; int dirty_slots_count = 0;
/* We should detect if sender is new master of our shard.
* We will know it if all our slots were migrated to sender, and sender
* has no slots except ours */
int sender_slots = 0;
int migrated_our_slots = 0;
/* Here we set curmaster to this node or the node this node /* Here we set curmaster to this node or the node this node
* replicates to if it's a slave. In the for loop we are * replicates to if it's a slave. In the for loop we are
* interested to check if slots are taken away from curmaster. */ * interested to check if slots are taken away from curmaster. */
...@@ -1633,6 +1662,8 @@ void clusterUpdateSlotsConfigWith(clusterNode *sender, uint64_t senderConfigEpoc ...@@ -1633,6 +1662,8 @@ void clusterUpdateSlotsConfigWith(clusterNode *sender, uint64_t senderConfigEpoc
for (j = 0; j < CLUSTER_SLOTS; j++) { for (j = 0; j < CLUSTER_SLOTS; j++) {
if (bitmapTestBit(slots,j)) { if (bitmapTestBit(slots,j)) {
sender_slots++;
/* The slot is already bound to the sender of this message. */ /* The slot is already bound to the sender of this message. */
if (server.cluster->slots[j] == sender) continue; if (server.cluster->slots[j] == sender) continue;
...@@ -1659,8 +1690,10 @@ void clusterUpdateSlotsConfigWith(clusterNode *sender, uint64_t senderConfigEpoc ...@@ -1659,8 +1690,10 @@ void clusterUpdateSlotsConfigWith(clusterNode *sender, uint64_t senderConfigEpoc
dirty_slots_count++; dirty_slots_count++;
} }
if (server.cluster->slots[j] == curmaster) if (server.cluster->slots[j] == curmaster) {
newmaster = sender; newmaster = sender;
migrated_our_slots++;
}
clusterDelSlot(j); clusterDelSlot(j);
clusterAddSlot(sender,j); clusterAddSlot(sender,j);
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG| clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|
...@@ -1683,7 +1716,9 @@ void clusterUpdateSlotsConfigWith(clusterNode *sender, uint64_t senderConfigEpoc ...@@ -1683,7 +1716,9 @@ void clusterUpdateSlotsConfigWith(clusterNode *sender, uint64_t senderConfigEpoc
* master. * master.
* 2) We are a slave and our master is left without slots. We need * 2) We are a slave and our master is left without slots. We need
* to replicate to the new slots owner. */ * to replicate to the new slots owner. */
if (newmaster && curmaster->numslots == 0) { if (newmaster && curmaster->numslots == 0 &&
(server.cluster_allow_replica_migration ||
sender_slots == migrated_our_slots)) {
serverLog(LL_WARNING, serverLog(LL_WARNING,
"Configuration change detected. Reconfiguring myself " "Configuration change detected. Reconfiguring myself "
"as a replica of %.40s", sender->name); "as a replica of %.40s", sender->name);
...@@ -1811,7 +1846,7 @@ int clusterProcessPacket(clusterLink *link) { ...@@ -1811,7 +1846,7 @@ int clusterProcessPacket(clusterLink *link) {
nodeIsSlave(myself) && nodeIsSlave(myself) &&
myself->slaveof == sender && myself->slaveof == sender &&
hdr->mflags[0] & CLUSTERMSG_FLAG0_PAUSED && hdr->mflags[0] & CLUSTERMSG_FLAG0_PAUSED &&
server.cluster->mf_master_offset == 0) server.cluster->mf_master_offset == -1)
{ {
server.cluster->mf_master_offset = sender->repl_offset; server.cluster->mf_master_offset = sender->repl_offset;
clusterDoBeforeSleep(CLUSTER_TODO_HANDLE_MANUALFAILOVER); clusterDoBeforeSleep(CLUSTER_TODO_HANDLE_MANUALFAILOVER);
...@@ -1862,6 +1897,7 @@ int clusterProcessPacket(clusterLink *link) { ...@@ -1862,6 +1897,7 @@ int clusterProcessPacket(clusterLink *link) {
node = createClusterNode(NULL,CLUSTER_NODE_HANDSHAKE); node = createClusterNode(NULL,CLUSTER_NODE_HANDSHAKE);
nodeIp2String(node->ip,link,hdr->myip); nodeIp2String(node->ip,link,hdr->myip);
node->port = ntohs(hdr->port); node->port = ntohs(hdr->port);
node->pport = ntohs(hdr->pport);
node->cport = ntohs(hdr->cport); node->cport = ntohs(hdr->cport);
clusterAddNode(node); clusterAddNode(node);
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG); clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);
...@@ -1924,6 +1960,7 @@ int clusterProcessPacket(clusterLink *link) { ...@@ -1924,6 +1960,7 @@ int clusterProcessPacket(clusterLink *link) {
link->node->flags |= CLUSTER_NODE_NOADDR; link->node->flags |= CLUSTER_NODE_NOADDR;
link->node->ip[0] = '\0'; link->node->ip[0] = '\0';
link->node->port = 0; link->node->port = 0;
link->node->pport = 0;
link->node->cport = 0; link->node->cport = 0;
freeClusterLink(link); freeClusterLink(link);
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG); clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);
...@@ -2423,19 +2460,16 @@ void clusterBuildMessageHdr(clusterMsg *hdr, int type) { ...@@ -2423,19 +2460,16 @@ void clusterBuildMessageHdr(clusterMsg *hdr, int type) {
hdr->myip[NET_IP_STR_LEN-1] = '\0'; hdr->myip[NET_IP_STR_LEN-1] = '\0';
} }
/* Handle cluster-announce-port as well. */ /* Handle cluster-announce-[tls-|bus-]port. */
int port = server.tls_cluster ? server.tls_port : server.port; int announced_port, announced_pport, announced_cport;
int announced_port = server.cluster_announce_port ? deriveAnnouncedPorts(&announced_port, &announced_pport, &announced_cport);
server.cluster_announce_port : port;
int announced_cport = server.cluster_announce_bus_port ?
server.cluster_announce_bus_port :
(port + CLUSTER_PORT_INCR);
memcpy(hdr->myslots,master->slots,sizeof(hdr->myslots)); memcpy(hdr->myslots,master->slots,sizeof(hdr->myslots));
memset(hdr->slaveof,0,CLUSTER_NAMELEN); memset(hdr->slaveof,0,CLUSTER_NAMELEN);
if (myself->slaveof != NULL) if (myself->slaveof != NULL)
memcpy(hdr->slaveof,myself->slaveof->name, CLUSTER_NAMELEN); memcpy(hdr->slaveof,myself->slaveof->name, CLUSTER_NAMELEN);
hdr->port = htons(announced_port); hdr->port = htons(announced_port);
hdr->pport = htons(announced_pport);
hdr->cport = htons(announced_cport); hdr->cport = htons(announced_cport);
hdr->flags = htons(myself->flags); hdr->flags = htons(myself->flags);
hdr->state = server.cluster->state; hdr->state = server.cluster->state;
...@@ -2492,6 +2526,7 @@ void clusterSetGossipEntry(clusterMsg *hdr, int i, clusterNode *n) { ...@@ -2492,6 +2526,7 @@ void clusterSetGossipEntry(clusterMsg *hdr, int i, clusterNode *n) {
gossip->port = htons(n->port); gossip->port = htons(n->port);
gossip->cport = htons(n->cport); gossip->cport = htons(n->cport);
gossip->flags = htons(n->flags); gossip->flags = htons(n->flags);
gossip->pport = htons(n->pport);
gossip->notused1 = 0; gossip->notused1 = 0;
} }
...@@ -3090,7 +3125,7 @@ void clusterFailoverReplaceYourMaster(void) { ...@@ -3090,7 +3125,7 @@ void clusterFailoverReplaceYourMaster(void) {
/* This function is called if we are a slave node and our master serving /* This function is called if we are a slave node and our master serving
* a non-zero amount of hash slots is in FAIL state. * a non-zero amount of hash slots is in FAIL state.
* *
* The gaol of this function is: * The goal of this function is:
* 1) To check if we are able to perform a failover, is our data updated? * 1) To check if we are able to perform a failover, is our data updated?
* 2) Try to get elected by masters. * 2) Try to get elected by masters.
* 3) Perform the failover informing all the other nodes. * 3) Perform the failover informing all the other nodes.
...@@ -3135,7 +3170,7 @@ void clusterHandleSlaveFailover(void) { ...@@ -3135,7 +3170,7 @@ void clusterHandleSlaveFailover(void) {
return; return;
} }
/* Set data_age to the number of seconds we are disconnected from /* Set data_age to the number of milliseconds we are disconnected from
* the master. */ * the master. */
if (server.repl_state == REPL_STATE_CONNECTED) { if (server.repl_state == REPL_STATE_CONNECTED) {
data_age = (mstime_t)(server.unixtime - server.master->lastinteraction) data_age = (mstime_t)(server.unixtime - server.master->lastinteraction)
...@@ -3419,7 +3454,7 @@ void resetManualFailover(void) { ...@@ -3419,7 +3454,7 @@ void resetManualFailover(void) {
server.cluster->mf_end = 0; /* No manual failover in progress. */ server.cluster->mf_end = 0; /* No manual failover in progress. */
server.cluster->mf_can_start = 0; server.cluster->mf_can_start = 0;
server.cluster->mf_slave = NULL; server.cluster->mf_slave = NULL;
server.cluster->mf_master_offset = 0; server.cluster->mf_master_offset = -1;
} }
/* If a manual failover timed out, abort it. */ /* If a manual failover timed out, abort it. */
...@@ -3440,7 +3475,7 @@ void clusterHandleManualFailover(void) { ...@@ -3440,7 +3475,7 @@ void clusterHandleManualFailover(void) {
* next steps are performed by clusterHandleSlaveFailover(). */ * next steps are performed by clusterHandleSlaveFailover(). */
if (server.cluster->mf_can_start) return; if (server.cluster->mf_can_start) return;
if (server.cluster->mf_master_offset == 0) return; /* Wait for offset... */ if (server.cluster->mf_master_offset == -1) return; /* Wait for offset... */
if (server.cluster->mf_master_offset == replicationGetSlaveOffset()) { if (server.cluster->mf_master_offset == replicationGetSlaveOffset()) {
/* Our replication offset matches the master replication offset /* Our replication offset matches the master replication offset
...@@ -3630,7 +3665,6 @@ void clusterCron(void) { ...@@ -3630,7 +3665,6 @@ void clusterCron(void) {
now - node->link->ctime > now - node->link->ctime >
server.cluster_node_timeout && /* was not already reconnected */ server.cluster_node_timeout && /* was not already reconnected */
node->ping_sent && /* we already sent a ping */ node->ping_sent && /* we already sent a ping */
node->pong_received < node->ping_sent && /* still waiting pong */
/* and we are waiting for the pong more than timeout/2 */ /* and we are waiting for the pong more than timeout/2 */
ping_delay > server.cluster_node_timeout/2 && ping_delay > server.cluster_node_timeout/2 &&
/* and in such interval we are not seeing any traffic at all. */ /* and in such interval we are not seeing any traffic at all. */
...@@ -3713,7 +3747,8 @@ void clusterCron(void) { ...@@ -3713,7 +3747,8 @@ void clusterCron(void) {
* the orphaned masters. Note that it does not make sense to try * the orphaned masters. Note that it does not make sense to try
* a migration if there is no master with at least *two* working * a migration if there is no master with at least *two* working
* slaves. */ * slaves. */
if (orphaned_masters && max_slaves >= 2 && this_slaves == max_slaves) if (orphaned_masters && max_slaves >= 2 && this_slaves == max_slaves &&
server.cluster_allow_replica_migration)
clusterHandleSlaveMigration(max_slaves); clusterHandleSlaveMigration(max_slaves);
} }
...@@ -3823,7 +3858,7 @@ int clusterNodeSetSlotBit(clusterNode *n, int slot) { ...@@ -3823,7 +3858,7 @@ int clusterNodeSetSlotBit(clusterNode *n, int slot) {
* However new masters with slots assigned are considered valid * However new masters with slots assigned are considered valid
* migration targets if the rest of the cluster is not a slave-less. * migration targets if the rest of the cluster is not a slave-less.
* *
* See https://github.com/antirez/redis/issues/3043 for more info. */ * See https://github.com/redis/redis/issues/3043 for more info. */
if (n->numslots == 1 && clusterMastersHaveSlaves()) if (n->numslots == 1 && clusterMastersHaveSlaves())
n->flags |= CLUSTER_NODE_MIGRATE_TO; n->flags |= CLUSTER_NODE_MIGRATE_TO;
} }
...@@ -4131,15 +4166,16 @@ sds representClusterNodeFlags(sds ci, uint16_t flags) { ...@@ -4131,15 +4166,16 @@ sds representClusterNodeFlags(sds ci, uint16_t flags) {
* See clusterGenNodesDescription() top comment for more information. * See clusterGenNodesDescription() top comment for more information.
* *
* The function returns the string representation as an SDS string. */ * The function returns the string representation as an SDS string. */
sds clusterGenNodeDescription(clusterNode *node) { sds clusterGenNodeDescription(clusterNode *node, int use_pport) {
int j, start; int j, start;
sds ci; sds ci;
int port = use_pport && node->pport ? node->pport : node->port;
/* Node coordinates */ /* Node coordinates */
ci = sdscatlen(sdsempty(),node->name,CLUSTER_NAMELEN); ci = sdscatlen(sdsempty(),node->name,CLUSTER_NAMELEN);
ci = sdscatfmt(ci," %s:%i@%i ", ci = sdscatfmt(ci," %s:%i@%i ",
node->ip, node->ip,
node->port, port,
node->cport); node->cport);
/* Flags */ /* Flags */
...@@ -4250,10 +4286,13 @@ void clusterGenNodesSlotsInfo(int filter) { ...@@ -4250,10 +4286,13 @@ void clusterGenNodesSlotsInfo(int filter) {
* include all the known nodes in the representation, including nodes in * include all the known nodes in the representation, including nodes in
* the HANDSHAKE state. * the HANDSHAKE state.
* *
* Setting use_pport to 1 in a TLS cluster makes the result contain the
* plaintext client port rather then the TLS client port of each node.
*
* The representation obtained using this function is used for the output * The representation obtained using this function is used for the output
* of the CLUSTER NODES function, and as format for the cluster * of the CLUSTER NODES function, and as format for the cluster
* configuration file (nodes.conf) for a given node. */ * configuration file (nodes.conf) for a given node. */
sds clusterGenNodesDescription(int filter) { sds clusterGenNodesDescription(int filter, int use_pport) {
sds ci = sdsempty(), ni; sds ci = sdsempty(), ni;
dictIterator *di; dictIterator *di;
dictEntry *de; dictEntry *de;
...@@ -4266,7 +4305,7 @@ sds clusterGenNodesDescription(int filter) { ...@@ -4266,7 +4305,7 @@ sds clusterGenNodesDescription(int filter) {
clusterNode *node = dictGetVal(de); clusterNode *node = dictGetVal(de);
if (node->flags & filter) continue; if (node->flags & filter) continue;
ni = clusterGenNodeDescription(node); ni = clusterGenNodeDescription(node, use_pport);
ci = sdscatsds(ci,ni); ci = sdscatsds(ci,ni);
sdsfree(ni); sdsfree(ni);
ci = sdscatlen(ci,"\n",1); ci = sdscatlen(ci,"\n",1);
...@@ -4313,7 +4352,37 @@ int getSlotOrReply(client *c, robj *o) { ...@@ -4313,7 +4352,37 @@ int getSlotOrReply(client *c, robj *o) {
return (int) slot; return (int) slot;
} }
void clusterReplyMultiBulkSlots(client *c) { void addNodeReplyForClusterSlot(client *c, clusterNode *node, int start_slot, int end_slot) {
int i, nested_elements = 3; /* slots (2) + master addr (1) */
void *nested_replylen = addReplyDeferredLen(c);
addReplyLongLong(c, start_slot);
addReplyLongLong(c, end_slot);
addReplyArrayLen(c, 3);
addReplyBulkCString(c, node->ip);
/* Report non-TLS ports to non-TLS client in TLS cluster if available. */
int use_pport = (server.tls_cluster &&
c->conn && connGetType(c->conn) != CONN_TYPE_TLS);
addReplyLongLong(c, use_pport && node->pport ? node->pport : node->port);
addReplyBulkCBuffer(c, node->name, CLUSTER_NAMELEN);
/* Remaining nodes in reply are replicas for slot range */
for (i = 0; i < node->numslaves; i++) {
/* This loop is copy/pasted from clusterGenNodeDescription()
* with modifications for per-slot node aggregation. */
if (nodeFailed(node->slaves[i])) continue;
addReplyArrayLen(c, 3);
addReplyBulkCString(c, node->slaves[i]->ip);
/* Report slave's non-TLS port to non-TLS client in TLS cluster */
addReplyLongLong(c, (use_pport && node->slaves[i]->pport ?
node->slaves[i]->pport :
node->slaves[i]->port));
addReplyBulkCBuffer(c, node->slaves[i]->name, CLUSTER_NAMELEN);
nested_elements++;
}
setDeferredArrayLen(c, nested_replylen, nested_elements);
}
void clusterReplyMultiBulkSlots(client * c) {
/* Format: 1) 1) start slot /* Format: 1) 1) start slot
* 2) end slot * 2) end slot
* 3) 1) master IP * 3) 1) master IP
...@@ -4324,69 +4393,29 @@ void clusterReplyMultiBulkSlots(client *c) { ...@@ -4324,69 +4393,29 @@ void clusterReplyMultiBulkSlots(client *c) {
* 3) node ID * 3) node ID
* ... continued until done * ... continued until done
*/ */
clusterNode *n = NULL;
int num_masters = 0; int num_masters = 0, start = -1;
void *slot_replylen = addReplyDeferredLen(c); void *slot_replylen = addReplyDeferredLen(c);
dictEntry *de; for (int i = 0; i <= CLUSTER_SLOTS; i++) {
dictIterator *di = dictGetSafeIterator(server.cluster->nodes); /* Find start node and slot id. */
while((de = dictNext(di)) != NULL) { if (n == NULL) {
clusterNode *node = dictGetVal(de); if (i == CLUSTER_SLOTS) break;
int j = 0, start = -1; n = server.cluster->slots[i];
int i, nested_elements = 0; start = i;
continue;
/* Skip slaves (that are iterated when producing the output of their
* master) and masters not serving any slot. */
if (!nodeIsMaster(node) || node->numslots == 0) continue;
for(i = 0; i < node->numslaves; i++) {
if (nodeFailed(node->slaves[i])) continue;
nested_elements++;
}
for (j = 0; j < CLUSTER_SLOTS; j++) {
int bit, i;
if ((bit = clusterNodeGetSlotBit(node,j)) != 0) {
if (start == -1) start = j;
}
if (start != -1 && (!bit || j == CLUSTER_SLOTS-1)) {
addReplyArrayLen(c, nested_elements + 3); /* slots (2) + master addr (1). */
if (bit && j == CLUSTER_SLOTS-1) j++;
/* If slot exists in output map, add to it's list.
* else, create a new output map for this slot */
if (start == j-1) {
addReplyLongLong(c, start); /* only one slot; low==high */
addReplyLongLong(c, start);
} else {
addReplyLongLong(c, start); /* low */
addReplyLongLong(c, j-1); /* high */
} }
start = -1;
/* First node reply position is always the master */
addReplyArrayLen(c, 3);
addReplyBulkCString(c, node->ip);
addReplyLongLong(c, node->port);
addReplyBulkCBuffer(c, node->name, CLUSTER_NAMELEN);
/* Remaining nodes in reply are replicas for slot range */ /* Add cluster slots info when occur different node with start
for (i = 0; i < node->numslaves; i++) { * or end of slot. */
/* This loop is copy/pasted from clusterGenNodeDescription() if (i == CLUSTER_SLOTS || n != server.cluster->slots[i]) {
* with modifications for per-slot node aggregation */ addNodeReplyForClusterSlot(c, n, start, i-1);
if (nodeFailed(node->slaves[i])) continue;
addReplyArrayLen(c, 3);
addReplyBulkCString(c, node->slaves[i]->ip);
addReplyLongLong(c, node->slaves[i]->port);
addReplyBulkCBuffer(c, node->slaves[i]->name, CLUSTER_NAMELEN);
}
num_masters++; num_masters++;
if (i == CLUSTER_SLOTS) break;
n = server.cluster->slots[i];
start = i;
} }
} }
}
dictReleaseIterator(di);
setDeferredArrayLen(c, slot_replylen, num_masters); setDeferredArrayLen(c, slot_replylen, num_masters);
} }
...@@ -4475,7 +4504,11 @@ NULL ...@@ -4475,7 +4504,11 @@ NULL
} }
} else if (!strcasecmp(c->argv[1]->ptr,"nodes") && c->argc == 2) { } else if (!strcasecmp(c->argv[1]->ptr,"nodes") && c->argc == 2) {
/* CLUSTER NODES */ /* CLUSTER NODES */
sds nodes = clusterGenNodesDescription(0); /* Report plaintext ports, only if cluster is TLS but client is known to
* be non-TLS). */
int use_pport = (server.tls_cluster &&
c->conn && connGetType(c->conn) != CONN_TYPE_TLS);
sds nodes = clusterGenNodesDescription(0, use_pport);
addReplyVerbatim(c,nodes,sdslen(nodes),"txt"); addReplyVerbatim(c,nodes,sdslen(nodes),"txt");
sdsfree(nodes); sdsfree(nodes);
} else if (!strcasecmp(c->argv[1]->ptr,"myid") && c->argc == 2) { } else if (!strcasecmp(c->argv[1]->ptr,"myid") && c->argc == 2) {
...@@ -4851,9 +4884,12 @@ NULL ...@@ -4851,9 +4884,12 @@ NULL
return; return;
} }
/* Use plaintext port if cluster is TLS but client is non-TLS. */
int use_pport = (server.tls_cluster &&
c->conn && connGetType(c->conn) != CONN_TYPE_TLS);
addReplyArrayLen(c,n->numslaves); addReplyArrayLen(c,n->numslaves);
for (j = 0; j < n->numslaves; j++) { for (j = 0; j < n->numslaves; j++) {
sds ni = clusterGenNodeDescription(n->slaves[j]); sds ni = clusterGenNodeDescription(n->slaves[j], use_pport);
addReplyBulkCString(c,ni); addReplyBulkCString(c,ni);
sdsfree(ni); sdsfree(ni);
} }
...@@ -5824,18 +5860,14 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in ...@@ -5824,18 +5860,14 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
* cluster is down. */ * cluster is down. */
if (error_code) *error_code = CLUSTER_REDIR_DOWN_STATE; if (error_code) *error_code = CLUSTER_REDIR_DOWN_STATE;
return NULL; return NULL;
} else if ((cmd->flags & CMD_WRITE) && !(cmd->proc == evalCommand) } else if (cmd->flags & CMD_WRITE) {
&& !(cmd->proc == evalShaCommand)) /* The cluster is configured to allow read only commands */
{
/* The cluster is configured to allow read only commands
* but this command is neither readonly, nor EVAL or
* EVALSHA. */
if (error_code) *error_code = CLUSTER_REDIR_DOWN_RO_STATE; if (error_code) *error_code = CLUSTER_REDIR_DOWN_RO_STATE;
return NULL; return NULL;
} else { } else {
/* Fall through and allow the command to be executed: /* Fall through and allow the command to be executed:
* this happens when server.cluster_allow_reads_when_down is * this happens when server.cluster_allow_reads_when_down is
* true and the command is a readonly command or EVAL / EVALSHA. */ * true and the command is not a write command */
} }
} }
...@@ -5876,7 +5908,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in ...@@ -5876,7 +5908,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
int is_write_command = (c->cmd->flags & CMD_WRITE) || int is_write_command = (c->cmd->flags & CMD_WRITE) ||
(c->cmd->proc == execCommand && (c->mstate.cmd_flags & CMD_WRITE)); (c->cmd->proc == execCommand && (c->mstate.cmd_flags & CMD_WRITE));
if (c->flags & CLIENT_READONLY && if (c->flags & CLIENT_READONLY &&
(!is_write_command || cmd->proc == evalCommand || cmd->proc == evalShaCommand) && !is_write_command &&
nodeIsSlave(myself) && nodeIsSlave(myself) &&
myself->slaveof == n) myself->slaveof == n)
{ {
...@@ -5913,10 +5945,15 @@ void clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_co ...@@ -5913,10 +5945,15 @@ void clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_co
} else if (error_code == CLUSTER_REDIR_MOVED || } else if (error_code == CLUSTER_REDIR_MOVED ||
error_code == CLUSTER_REDIR_ASK) error_code == CLUSTER_REDIR_ASK)
{ {
/* Redirect to IP:port. Include plaintext port if cluster is TLS but
* client is non-TLS. */
int use_pport = (server.tls_cluster &&
c->conn && connGetType(c->conn) != CONN_TYPE_TLS);
int port = use_pport && n->pport ? n->pport : n->port;
addReplyErrorSds(c,sdscatprintf(sdsempty(), addReplyErrorSds(c,sdscatprintf(sdsempty(),
"-%s %d %s:%d", "-%s %d %s:%d",
(error_code == CLUSTER_REDIR_ASK) ? "ASK" : "MOVED", (error_code == CLUSTER_REDIR_ASK) ? "ASK" : "MOVED",
hashslot,n->ip,n->port)); hashslot, n->ip, port));
} else { } else {
serverPanic("getNodeByQuery() unknown error."); serverPanic("getNodeByQuery() unknown error.");
} }
......
...@@ -135,7 +135,9 @@ typedef struct clusterNode { ...@@ -135,7 +135,9 @@ typedef struct clusterNode {
mstime_t orphaned_time; /* Starting time of orphaned master condition */ mstime_t orphaned_time; /* Starting time of orphaned master condition */
long long repl_offset; /* Last known repl offset for this node. */ long long repl_offset; /* Last known repl offset for this node. */
char ip[NET_IP_STR_LEN]; /* Latest known IP address of this node */ char ip[NET_IP_STR_LEN]; /* Latest known IP address of this node */
int port; /* Latest known clients port of this node */ int port; /* Latest known clients port (TLS or plain). */
int pport; /* Latest known clients plaintext port. Only used
if the main clients port is for TLS. */
int cport; /* Latest known cluster port of this node. */ int cport; /* Latest known cluster port of this node. */
clusterLink *link; /* TCP/IP link with this node */ clusterLink *link; /* TCP/IP link with this node */
list *fail_reports; /* List of nodes signaling this as failing */ list *fail_reports; /* List of nodes signaling this as failing */
...@@ -168,7 +170,7 @@ typedef struct clusterState { ...@@ -168,7 +170,7 @@ typedef struct clusterState {
clusterNode *mf_slave; /* Slave performing the manual failover. */ clusterNode *mf_slave; /* Slave performing the manual failover. */
/* Manual failover state of slave. */ /* Manual failover state of slave. */
long long mf_master_offset; /* Master offset the slave needs to start MF long long mf_master_offset; /* Master offset the slave needs to start MF
or zero if still not received. */ or -1 if still not received. */
int mf_can_start; /* If non-zero signal that the manual failover int mf_can_start; /* If non-zero signal that the manual failover
can start requesting masters vote. */ can start requesting masters vote. */
/* The following fields are used by masters to take state on elections. */ /* The following fields are used by masters to take state on elections. */
...@@ -194,7 +196,8 @@ typedef struct { ...@@ -194,7 +196,8 @@ typedef struct {
uint16_t port; /* base port last time it was seen */ uint16_t port; /* base port last time it was seen */
uint16_t cport; /* cluster port last time it was seen */ uint16_t cport; /* cluster port last time it was seen */
uint16_t flags; /* node->flags copy */ uint16_t flags; /* node->flags copy */
uint32_t notused1; uint16_t pport; /* plaintext-port, when base port is TLS */
uint16_t notused1;
} clusterMsgDataGossip; } clusterMsgDataGossip;
typedef struct { typedef struct {
...@@ -267,7 +270,8 @@ typedef struct { ...@@ -267,7 +270,8 @@ typedef struct {
unsigned char myslots[CLUSTER_SLOTS/8]; unsigned char myslots[CLUSTER_SLOTS/8];
char slaveof[CLUSTER_NAMELEN]; char slaveof[CLUSTER_NAMELEN];
char myip[NET_IP_STR_LEN]; /* Sender IP, if not all zeroed. */ char myip[NET_IP_STR_LEN]; /* Sender IP, if not all zeroed. */
char notused1[34]; /* 34 bytes reserved for future usage. */ char notused1[32]; /* 32 bytes reserved for future usage. */
uint16_t pport; /* Sender TCP plaintext port, if base port is TLS */
uint16_t cport; /* Sender TCP cluster bus port */ uint16_t cport; /* Sender TCP cluster bus port */
uint16_t flags; /* Sender node flags */ uint16_t flags; /* Sender node flags */
unsigned char state; /* Cluster state from the POV of the sender */ unsigned char state; /* Cluster state from the POV of the sender */
......
...@@ -229,8 +229,6 @@ typedef union typeData { ...@@ -229,8 +229,6 @@ typedef union typeData {
typedef struct typeInterface { typedef struct typeInterface {
/* Called on server start, to init the server with default value */ /* Called on server start, to init the server with default value */
void (*init)(typeData data); void (*init)(typeData data);
/* Called on server start, should return 1 on success, 0 on error and should set err */
int (*load)(typeData data, sds *argc, int argv, const char **err);
/* Called on server startup and CONFIG SET, returns 1 on success, 0 on error /* Called on server startup and CONFIG SET, returns 1 on success, 0 on error
* and can set a verbose err string, update is true when called from CONFIG SET */ * and can set a verbose err string, update is true when called from CONFIG SET */
int (*set)(typeData data, sds value, int update, const char **err); int (*set)(typeData data, sds value, int update, const char **err);
...@@ -243,11 +241,16 @@ typedef struct typeInterface { ...@@ -243,11 +241,16 @@ typedef struct typeInterface {
typedef struct standardConfig { typedef struct standardConfig {
const char *name; /* The user visible name of this config */ const char *name; /* The user visible name of this config */
const char *alias; /* An alias that can also be used for this config */ const char *alias; /* An alias that can also be used for this config */
const int modifiable; /* Can this value be updated by CONFIG SET? */ const unsigned int flags; /* Flags for this specific config */
typeInterface interface; /* The function pointers that define the type interface */ typeInterface interface; /* The function pointers that define the type interface */
typeData data; /* The type specific data exposed used by the interface */ typeData data; /* The type specific data exposed used by the interface */
} standardConfig; } standardConfig;
#define MODIFIABLE_CONFIG 0 /* This is the implied default for a standard
* config, which is mutable. */
#define IMMUTABLE_CONFIG (1ULL<<0) /* Can this value only be set at startup? */
#define SENSITIVE_CONFIG (1ULL<<1) /* Does this value contain sensitive information */
standardConfig configs[]; standardConfig configs[];
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
...@@ -618,6 +621,10 @@ void loadServerConfigFromString(char *config) { ...@@ -618,6 +621,10 @@ void loadServerConfigFromString(char *config) {
goto loaderr; goto loaderr;
} }
/* To ensure backward compatibility and work while hz is out of range */
if (server.config_hz < CONFIG_MIN_HZ) server.config_hz = CONFIG_MIN_HZ;
if (server.config_hz > CONFIG_MAX_HZ) server.config_hz = CONFIG_MAX_HZ;
sdsfreesplitres(lines,totlines); sdsfreesplitres(lines,totlines);
return; return;
...@@ -714,9 +721,13 @@ void configSetCommand(client *c) { ...@@ -714,9 +721,13 @@ void configSetCommand(client *c) {
/* Iterate the configs that are standard */ /* Iterate the configs that are standard */
for (standardConfig *config = configs; config->name != NULL; config++) { for (standardConfig *config = configs; config->name != NULL; config++) {
if(config->modifiable && (!strcasecmp(c->argv[2]->ptr,config->name) || if (!(config->flags & IMMUTABLE_CONFIG) &&
(!strcasecmp(c->argv[2]->ptr,config->name) ||
(config->alias && !strcasecmp(c->argv[2]->ptr,config->alias)))) (config->alias && !strcasecmp(c->argv[2]->ptr,config->alias))))
{ {
if (config->flags & SENSITIVE_CONFIG) {
preventCommandLogging(c);
}
if (!config->interface.set(config->data,o->ptr,1,&errstr)) { if (!config->interface.set(config->data,o->ptr,1,&errstr)) {
goto badfmt; goto badfmt;
} }
...@@ -1370,14 +1381,19 @@ void rewriteConfigSaveOption(struct rewriteConfigState *state) { ...@@ -1370,14 +1381,19 @@ void rewriteConfigSaveOption(struct rewriteConfigState *state) {
return; return;
} }
/* Note that if there are no save parameters at all, all the current /* Rewrite save parameters, or an empty 'save ""' line to avoid the
* config line with "save" will be detected as orphaned and deleted, * defaults from being used.
* resulting into no RDB persistence as expected. */ */
if (!server.saveparamslen) {
rewriteConfigRewriteLine(state,"save",sdsnew("save \"\""),1);
} else {
for (j = 0; j < server.saveparamslen; j++) { for (j = 0; j < server.saveparamslen; j++) {
line = sdscatprintf(sdsempty(),"save %ld %d", line = sdscatprintf(sdsempty(),"save %ld %d",
(long) server.saveparams[j].seconds, server.saveparams[j].changes); (long) server.saveparams[j].seconds, server.saveparams[j].changes);
rewriteConfigRewriteLine(state,"save",line,1); rewriteConfigRewriteLine(state,"save",line,1);
} }
}
/* Mark "save" as processed in case server.saveparamslen is zero. */ /* Mark "save" as processed in case server.saveparamslen is zero. */
rewriteConfigMarkAsProcessed(state,"save"); rewriteConfigMarkAsProcessed(state,"save");
} }
...@@ -1711,13 +1727,10 @@ int rewriteConfig(char *path, int force_all) { ...@@ -1711,13 +1727,10 @@ int rewriteConfig(char *path, int force_all) {
#define LOADBUF_SIZE 256 #define LOADBUF_SIZE 256
static char loadbuf[LOADBUF_SIZE]; static char loadbuf[LOADBUF_SIZE];
#define MODIFIABLE_CONFIG 1 #define embedCommonConfig(config_name, config_alias, config_flags) \
#define IMMUTABLE_CONFIG 0
#define embedCommonConfig(config_name, config_alias, is_modifiable) \
.name = (config_name), \ .name = (config_name), \
.alias = (config_alias), \ .alias = (config_alias), \
.modifiable = (is_modifiable), .flags = (config_flags),
#define embedConfigInterface(initfn, setfn, getfn, rewritefn) .interface = { \ #define embedConfigInterface(initfn, setfn, getfn, rewritefn) .interface = { \
.init = (initfn), \ .init = (initfn), \
...@@ -1768,8 +1781,8 @@ static void boolConfigRewrite(typeData data, const char *name, struct rewriteCon ...@@ -1768,8 +1781,8 @@ static void boolConfigRewrite(typeData data, const char *name, struct rewriteCon
rewriteConfigYesNoOption(state, name,*(data.yesno.config), data.yesno.default_value); rewriteConfigYesNoOption(state, name,*(data.yesno.config), data.yesno.default_value);
} }
#define createBoolConfig(name, alias, modifiable, config_addr, default, is_valid, update) { \ #define createBoolConfig(name, alias, flags, config_addr, default, is_valid, update) { \
embedCommonConfig(name, alias, modifiable) \ embedCommonConfig(name, alias, flags) \
embedConfigInterface(boolConfigInit, boolConfigSet, boolConfigGet, boolConfigRewrite) \ embedConfigInterface(boolConfigInit, boolConfigSet, boolConfigGet, boolConfigRewrite) \
.data.yesno = { \ .data.yesno = { \
.config = &(config_addr), \ .config = &(config_addr), \
...@@ -1841,8 +1854,8 @@ static void sdsConfigRewrite(typeData data, const char *name, struct rewriteConf ...@@ -1841,8 +1854,8 @@ static void sdsConfigRewrite(typeData data, const char *name, struct rewriteConf
#define ALLOW_EMPTY_STRING 0 #define ALLOW_EMPTY_STRING 0
#define EMPTY_STRING_IS_NULL 1 #define EMPTY_STRING_IS_NULL 1
#define createStringConfig(name, alias, modifiable, empty_to_null, config_addr, default, is_valid, update) { \ #define createStringConfig(name, alias, flags, empty_to_null, config_addr, default, is_valid, update) { \
embedCommonConfig(name, alias, modifiable) \ embedCommonConfig(name, alias, flags) \
embedConfigInterface(stringConfigInit, stringConfigSet, stringConfigGet, stringConfigRewrite) \ embedConfigInterface(stringConfigInit, stringConfigSet, stringConfigGet, stringConfigRewrite) \
.data.string = { \ .data.string = { \
.config = &(config_addr), \ .config = &(config_addr), \
...@@ -1853,8 +1866,8 @@ static void sdsConfigRewrite(typeData data, const char *name, struct rewriteConf ...@@ -1853,8 +1866,8 @@ static void sdsConfigRewrite(typeData data, const char *name, struct rewriteConf
} \ } \
} }
#define createSDSConfig(name, alias, modifiable, empty_to_null, config_addr, default, is_valid, update) { \ #define createSDSConfig(name, alias, flags, empty_to_null, config_addr, default, is_valid, update) { \
embedCommonConfig(name, alias, modifiable) \ embedCommonConfig(name, alias, flags) \
embedConfigInterface(sdsConfigInit, sdsConfigSet, sdsConfigGet, sdsConfigRewrite) \ embedConfigInterface(sdsConfigInit, sdsConfigSet, sdsConfigGet, sdsConfigRewrite) \
.data.sds = { \ .data.sds = { \
.config = &(config_addr), \ .config = &(config_addr), \
...@@ -1909,8 +1922,8 @@ static void enumConfigRewrite(typeData data, const char *name, struct rewriteCon ...@@ -1909,8 +1922,8 @@ static void enumConfigRewrite(typeData data, const char *name, struct rewriteCon
rewriteConfigEnumOption(state, name,*(data.enumd.config), data.enumd.enum_value, data.enumd.default_value); rewriteConfigEnumOption(state, name,*(data.enumd.config), data.enumd.enum_value, data.enumd.default_value);
} }
#define createEnumConfig(name, alias, modifiable, enum, config_addr, default, is_valid, update) { \ #define createEnumConfig(name, alias, flags, enum, config_addr, default, is_valid, update) { \
embedCommonConfig(name, alias, modifiable) \ embedCommonConfig(name, alias, flags) \
embedConfigInterface(enumConfigInit, enumConfigSet, enumConfigGet, enumConfigRewrite) \ embedConfigInterface(enumConfigInit, enumConfigSet, enumConfigGet, enumConfigRewrite) \
.data.enumd = { \ .data.enumd = { \
.config = &(config_addr), \ .config = &(config_addr), \
...@@ -2063,8 +2076,8 @@ static void numericConfigRewrite(typeData data, const char *name, struct rewrite ...@@ -2063,8 +2076,8 @@ static void numericConfigRewrite(typeData data, const char *name, struct rewrite
#define INTEGER_CONFIG 0 #define INTEGER_CONFIG 0
#define MEMORY_CONFIG 1 #define MEMORY_CONFIG 1
#define embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) { \ #define embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) { \
embedCommonConfig(name, alias, modifiable) \ embedCommonConfig(name, alias, flags) \
embedConfigInterface(numericConfigInit, numericConfigSet, numericConfigGet, numericConfigRewrite) \ embedConfigInterface(numericConfigInit, numericConfigSet, numericConfigGet, numericConfigRewrite) \
.data.numeric = { \ .data.numeric = { \
.lower_bound = (lower), \ .lower_bound = (lower), \
...@@ -2074,71 +2087,71 @@ static void numericConfigRewrite(typeData data, const char *name, struct rewrite ...@@ -2074,71 +2087,71 @@ static void numericConfigRewrite(typeData data, const char *name, struct rewrite
.update_fn = (update), \ .update_fn = (update), \
.is_memory = (memory), .is_memory = (memory),
#define createIntConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ #define createIntConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
.numeric_type = NUMERIC_TYPE_INT, \ .numeric_type = NUMERIC_TYPE_INT, \
.config.i = &(config_addr) \ .config.i = &(config_addr) \
} \ } \
} }
#define createUIntConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ #define createUIntConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
.numeric_type = NUMERIC_TYPE_UINT, \ .numeric_type = NUMERIC_TYPE_UINT, \
.config.ui = &(config_addr) \ .config.ui = &(config_addr) \
} \ } \
} }
#define createLongConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ #define createLongConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
.numeric_type = NUMERIC_TYPE_LONG, \ .numeric_type = NUMERIC_TYPE_LONG, \
.config.l = &(config_addr) \ .config.l = &(config_addr) \
} \ } \
} }
#define createULongConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ #define createULongConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
.numeric_type = NUMERIC_TYPE_ULONG, \ .numeric_type = NUMERIC_TYPE_ULONG, \
.config.ul = &(config_addr) \ .config.ul = &(config_addr) \
} \ } \
} }
#define createLongLongConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ #define createLongLongConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
.numeric_type = NUMERIC_TYPE_LONG_LONG, \ .numeric_type = NUMERIC_TYPE_LONG_LONG, \
.config.ll = &(config_addr) \ .config.ll = &(config_addr) \
} \ } \
} }
#define createULongLongConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ #define createULongLongConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
.numeric_type = NUMERIC_TYPE_ULONG_LONG, \ .numeric_type = NUMERIC_TYPE_ULONG_LONG, \
.config.ull = &(config_addr) \ .config.ull = &(config_addr) \
} \ } \
} }
#define createSizeTConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ #define createSizeTConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
.numeric_type = NUMERIC_TYPE_SIZE_T, \ .numeric_type = NUMERIC_TYPE_SIZE_T, \
.config.st = &(config_addr) \ .config.st = &(config_addr) \
} \ } \
} }
#define createSSizeTConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ #define createSSizeTConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
.numeric_type = NUMERIC_TYPE_SSIZE_T, \ .numeric_type = NUMERIC_TYPE_SSIZE_T, \
.config.sst = &(config_addr) \ .config.sst = &(config_addr) \
} \ } \
} }
#define createTimeTConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ #define createTimeTConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
.numeric_type = NUMERIC_TYPE_TIME_T, \ .numeric_type = NUMERIC_TYPE_TIME_T, \
.config.tt = &(config_addr) \ .config.tt = &(config_addr) \
} \ } \
} }
#define createOffTConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ #define createOffTConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, memory, is_valid, update) \
.numeric_type = NUMERIC_TYPE_OFF_T, \ .numeric_type = NUMERIC_TYPE_OFF_T, \
.config.ot = &(config_addr) \ .config.ot = &(config_addr) \
} \ } \
...@@ -2425,13 +2438,15 @@ standardConfig configs[] = { ...@@ -2425,13 +2438,15 @@ standardConfig configs[] = {
createBoolConfig("crash-memcheck-enabled", NULL, MODIFIABLE_CONFIG, server.memcheck_enabled, 1, NULL, NULL), createBoolConfig("crash-memcheck-enabled", NULL, MODIFIABLE_CONFIG, server.memcheck_enabled, 1, NULL, NULL),
createBoolConfig("use-exit-on-panic", NULL, MODIFIABLE_CONFIG, server.use_exit_on_panic, 0, NULL, NULL), createBoolConfig("use-exit-on-panic", NULL, MODIFIABLE_CONFIG, server.use_exit_on_panic, 0, NULL, NULL),
createBoolConfig("disable-thp", NULL, MODIFIABLE_CONFIG, server.disable_thp, 1, NULL, NULL), createBoolConfig("disable-thp", NULL, MODIFIABLE_CONFIG, server.disable_thp, 1, NULL, NULL),
createBoolConfig("cluster-allow-replica-migration", NULL, MODIFIABLE_CONFIG, server.cluster_allow_replica_migration, 1, NULL, NULL),
createBoolConfig("replica-announced", NULL, MODIFIABLE_CONFIG, server.replica_announced, 1, NULL, NULL),
/* String Configs */ /* String Configs */
createStringConfig("aclfile", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.acl_filename, "", NULL, NULL), createStringConfig("aclfile", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.acl_filename, "", NULL, NULL),
createStringConfig("unixsocket", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.unixsocket, NULL, NULL, NULL), createStringConfig("unixsocket", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.unixsocket, NULL, NULL, NULL),
createStringConfig("pidfile", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.pidfile, NULL, NULL, NULL), createStringConfig("pidfile", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.pidfile, NULL, NULL, NULL),
createStringConfig("replica-announce-ip", "slave-announce-ip", MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.slave_announce_ip, NULL, NULL, NULL), createStringConfig("replica-announce-ip", "slave-announce-ip", MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.slave_announce_ip, NULL, NULL, NULL),
createStringConfig("masteruser", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.masteruser, NULL, NULL, NULL), createStringConfig("masteruser", NULL, MODIFIABLE_CONFIG | SENSITIVE_CONFIG, EMPTY_STRING_IS_NULL, server.masteruser, NULL, NULL, NULL),
createStringConfig("cluster-announce-ip", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.cluster_announce_ip, NULL, NULL, NULL), createStringConfig("cluster-announce-ip", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.cluster_announce_ip, NULL, NULL, NULL),
createStringConfig("syslog-ident", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.syslog_ident, "redis", NULL, NULL), createStringConfig("syslog-ident", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.syslog_ident, "redis", NULL, NULL),
createStringConfig("dbfilename", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, server.rdb_filename, "dump.rdb", isValidDBfilename, NULL), createStringConfig("dbfilename", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, server.rdb_filename, "dump.rdb", isValidDBfilename, NULL),
...@@ -2444,8 +2459,8 @@ standardConfig configs[] = { ...@@ -2444,8 +2459,8 @@ standardConfig configs[] = {
createStringConfig("proc-title-template", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, server.proc_title_template, CONFIG_DEFAULT_PROC_TITLE_TEMPLATE, isValidProcTitleTemplate, updateProcTitleTemplate), createStringConfig("proc-title-template", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, server.proc_title_template, CONFIG_DEFAULT_PROC_TITLE_TEMPLATE, isValidProcTitleTemplate, updateProcTitleTemplate),
/* SDS Configs */ /* SDS Configs */
createSDSConfig("masterauth", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.masterauth, NULL, NULL, NULL), createSDSConfig("masterauth", NULL, MODIFIABLE_CONFIG | SENSITIVE_CONFIG, EMPTY_STRING_IS_NULL, server.masterauth, NULL, NULL, NULL),
createSDSConfig("requirepass", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.requirepass, NULL, NULL, updateRequirePass), createSDSConfig("requirepass", NULL, MODIFIABLE_CONFIG | SENSITIVE_CONFIG, EMPTY_STRING_IS_NULL, server.requirepass, NULL, NULL, updateRequirePass),
/* Enum Configs */ /* Enum Configs */
createEnumConfig("supervised", NULL, IMMUTABLE_CONFIG, supervised_mode_enum, server.supervised_mode, SUPERVISED_NONE, NULL, NULL), createEnumConfig("supervised", NULL, IMMUTABLE_CONFIG, supervised_mode_enum, server.supervised_mode, SUPERVISED_NONE, NULL, NULL),
...@@ -2455,7 +2470,7 @@ standardConfig configs[] = { ...@@ -2455,7 +2470,7 @@ standardConfig configs[] = {
createEnumConfig("maxmemory-policy", NULL, MODIFIABLE_CONFIG, maxmemory_policy_enum, server.maxmemory_policy, MAXMEMORY_NO_EVICTION, NULL, NULL), createEnumConfig("maxmemory-policy", NULL, MODIFIABLE_CONFIG, maxmemory_policy_enum, server.maxmemory_policy, MAXMEMORY_NO_EVICTION, NULL, NULL),
createEnumConfig("appendfsync", NULL, MODIFIABLE_CONFIG, aof_fsync_enum, server.aof_fsync, AOF_FSYNC_EVERYSEC, NULL, NULL), createEnumConfig("appendfsync", NULL, MODIFIABLE_CONFIG, aof_fsync_enum, server.aof_fsync, AOF_FSYNC_EVERYSEC, NULL, NULL),
createEnumConfig("oom-score-adj", NULL, MODIFIABLE_CONFIG, oom_score_adj_enum, server.oom_score_adj, OOM_SCORE_ADJ_NO, NULL, updateOOMScoreAdj), createEnumConfig("oom-score-adj", NULL, MODIFIABLE_CONFIG, oom_score_adj_enum, server.oom_score_adj, OOM_SCORE_ADJ_NO, NULL, updateOOMScoreAdj),
createEnumConfig("acl-pubsub-default", NULL, MODIFIABLE_CONFIG, acl_pubsub_default_enum, server.acl_pubusub_default, USER_FLAG_ALLCHANNELS, NULL, NULL), createEnumConfig("acl-pubsub-default", NULL, MODIFIABLE_CONFIG, acl_pubsub_default_enum, server.acl_pubsub_default, USER_FLAG_ALLCHANNELS, NULL, NULL),
createEnumConfig("sanitize-dump-payload", NULL, MODIFIABLE_CONFIG, sanitize_dump_payload_enum, server.sanitize_dump_payload, SANITIZE_DUMP_NO, NULL, NULL), createEnumConfig("sanitize-dump-payload", NULL, MODIFIABLE_CONFIG, sanitize_dump_payload_enum, server.sanitize_dump_payload, SANITIZE_DUMP_NO, NULL, NULL),
/* Integer configs */ /* Integer configs */
...@@ -2482,6 +2497,7 @@ standardConfig configs[] = { ...@@ -2482,6 +2497,7 @@ standardConfig configs[] = {
createIntConfig("tcp-backlog", NULL, IMMUTABLE_CONFIG, 0, INT_MAX, server.tcp_backlog, 511, INTEGER_CONFIG, NULL, NULL), /* TCP listen backlog. */ createIntConfig("tcp-backlog", NULL, IMMUTABLE_CONFIG, 0, INT_MAX, server.tcp_backlog, 511, INTEGER_CONFIG, NULL, NULL), /* TCP listen backlog. */
createIntConfig("cluster-announce-bus-port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.cluster_announce_bus_port, 0, INTEGER_CONFIG, NULL, NULL), /* Default: Use +10000 offset. */ createIntConfig("cluster-announce-bus-port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.cluster_announce_bus_port, 0, INTEGER_CONFIG, NULL, NULL), /* Default: Use +10000 offset. */
createIntConfig("cluster-announce-port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.cluster_announce_port, 0, INTEGER_CONFIG, NULL, NULL), /* Use server.port */ createIntConfig("cluster-announce-port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.cluster_announce_port, 0, INTEGER_CONFIG, NULL, NULL), /* Use server.port */
createIntConfig("cluster-announce-tls-port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.cluster_announce_tls_port, 0, INTEGER_CONFIG, NULL, NULL), /* Use server.tls_port */
createIntConfig("repl-timeout", NULL, MODIFIABLE_CONFIG, 1, INT_MAX, server.repl_timeout, 60, INTEGER_CONFIG, NULL, NULL), createIntConfig("repl-timeout", NULL, MODIFIABLE_CONFIG, 1, INT_MAX, server.repl_timeout, 60, INTEGER_CONFIG, NULL, NULL),
createIntConfig("repl-ping-replica-period", "repl-ping-slave-period", MODIFIABLE_CONFIG, 1, INT_MAX, server.repl_ping_slave_period, 10, INTEGER_CONFIG, NULL, NULL), createIntConfig("repl-ping-replica-period", "repl-ping-slave-period", MODIFIABLE_CONFIG, 1, INT_MAX, server.repl_ping_slave_period, 10, INTEGER_CONFIG, NULL, NULL),
createIntConfig("list-compress-depth", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.list_compress_depth, 0, INTEGER_CONFIG, NULL, NULL), createIntConfig("list-compress-depth", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.list_compress_depth, 0, INTEGER_CONFIG, NULL, NULL),
...@@ -2539,8 +2555,10 @@ standardConfig configs[] = { ...@@ -2539,8 +2555,10 @@ standardConfig configs[] = {
createBoolConfig("tls-session-caching", NULL, MODIFIABLE_CONFIG, server.tls_ctx_config.session_caching, 1, NULL, updateTlsCfgBool), createBoolConfig("tls-session-caching", NULL, MODIFIABLE_CONFIG, server.tls_ctx_config.session_caching, 1, NULL, updateTlsCfgBool),
createStringConfig("tls-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.cert_file, NULL, NULL, updateTlsCfg), createStringConfig("tls-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.cert_file, NULL, NULL, updateTlsCfg),
createStringConfig("tls-key-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.key_file, NULL, NULL, updateTlsCfg), createStringConfig("tls-key-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.key_file, NULL, NULL, updateTlsCfg),
createStringConfig("tls-key-file-pass", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.key_file_pass, NULL, NULL, updateTlsCfg),
createStringConfig("tls-client-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_cert_file, NULL, NULL, updateTlsCfg), createStringConfig("tls-client-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_cert_file, NULL, NULL, updateTlsCfg),
createStringConfig("tls-client-key-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_key_file, NULL, NULL, updateTlsCfg), createStringConfig("tls-client-key-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_key_file, NULL, NULL, updateTlsCfg),
createStringConfig("tls-client-key-file-pass", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_key_file_pass, NULL, NULL, updateTlsCfg),
createStringConfig("tls-dh-params-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.dh_params_file, NULL, NULL, updateTlsCfg), createStringConfig("tls-dh-params-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.dh_params_file, NULL, NULL, updateTlsCfg),
createStringConfig("tls-ca-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_file, NULL, NULL, updateTlsCfg), createStringConfig("tls-ca-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_file, NULL, NULL, updateTlsCfg),
createStringConfig("tls-ca-cert-dir", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_dir, NULL, NULL, updateTlsCfg), createStringConfig("tls-ca-cert-dir", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_dir, NULL, NULL, updateTlsCfg),
......
...@@ -127,9 +127,10 @@ uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) { ...@@ -127,9 +127,10 @@ uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) {
#include <stdio.h> #include <stdio.h>
#define UNUSED(x) (void)(x) #define UNUSED(x) (void)(x)
int crc64Test(int argc, char *argv[]) { int crc64Test(int argc, char *argv[], int accurate) {
UNUSED(argc); UNUSED(argc);
UNUSED(argv); UNUSED(argv);
UNUSED(accurate);
crc64_init(); crc64_init();
printf("[calcula]: e9c6d914c4b8d9ca == %016" PRIx64 "\n", printf("[calcula]: e9c6d914c4b8d9ca == %016" PRIx64 "\n",
(uint64_t)_crc64(0, "123456789", 9)); (uint64_t)_crc64(0, "123456789", 9));
......
...@@ -7,7 +7,7 @@ void crc64_init(void); ...@@ -7,7 +7,7 @@ void crc64_init(void);
uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l); uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
#ifdef REDIS_TEST #ifdef REDIS_TEST
int crc64Test(int argc, char *argv[]); int crc64Test(int argc, char *argv[], int accurate);
#endif #endif
#endif #endif
...@@ -139,9 +139,9 @@ robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) { ...@@ -139,9 +139,9 @@ robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
keymiss: keymiss:
if (!(flags & LOOKUP_NONOTIFY)) { if (!(flags & LOOKUP_NONOTIFY)) {
server.stat_keyspace_misses++;
notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id); notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id);
} }
server.stat_keyspace_misses++;
return NULL; return NULL;
} }
...@@ -164,16 +164,24 @@ robj *lookupKeyWriteWithFlags(redisDb *db, robj *key, int flags) { ...@@ -164,16 +164,24 @@ robj *lookupKeyWriteWithFlags(redisDb *db, robj *key, int flags) {
robj *lookupKeyWrite(redisDb *db, robj *key) { robj *lookupKeyWrite(redisDb *db, robj *key) {
return lookupKeyWriteWithFlags(db, key, LOOKUP_NONE); return lookupKeyWriteWithFlags(db, key, LOOKUP_NONE);
} }
static void SentReplyOnKeyMiss(client *c, robj *reply){
serverAssert(sdsEncodedObject(reply));
sds rep = reply->ptr;
if (sdslen(rep) > 1 && rep[0] == '-'){
addReplyErrorObject(c, reply);
} else {
addReply(c,reply);
}
}
robj *lookupKeyReadOrReply(client *c, robj *key, robj *reply) { robj *lookupKeyReadOrReply(client *c, robj *key, robj *reply) {
robj *o = lookupKeyRead(c->db, key); robj *o = lookupKeyRead(c->db, key);
if (!o) addReply(c,reply); if (!o) SentReplyOnKeyMiss(c, reply);
return o; return o;
} }
robj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply) { robj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply) {
robj *o = lookupKeyWrite(c->db, key); robj *o = lookupKeyWrite(c->db, key);
if (!o) addReply(c,reply); if (!o) SentReplyOnKeyMiss(c, reply);
return o; return o;
} }
...@@ -1445,7 +1453,12 @@ void propagateExpire(redisDb *db, robj *key, int lazy) { ...@@ -1445,7 +1453,12 @@ void propagateExpire(redisDb *db, robj *key, int lazy) {
incrRefCount(argv[0]); incrRefCount(argv[0]);
incrRefCount(argv[1]); incrRefCount(argv[1]);
/* If the master decided to expire a key we must propagate it to replicas no matter what..
* Even if module executed a command without asking for propagation. */
int prev_replication_allowed = server.replication_allowed;
server.replication_allowed = 1;
propagate(server.delCommand,db->id,argv,2,PROPAGATE_AOF|PROPAGATE_REPL); propagate(server.delCommand,db->id,argv,2,PROPAGATE_AOF|PROPAGATE_REPL);
server.replication_allowed = prev_replication_allowed;
decrRefCount(argv[0]); decrRefCount(argv[0]);
decrRefCount(argv[1]); decrRefCount(argv[1]);
......
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