Unverified Commit 959d6035 authored by Oran Agra's avatar Oran Agra Committed by GitHub
Browse files

Merge 6.2.2 release

Release 6.2.2
parents 92bde124 aa730ef1
...@@ -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
......
...@@ -11,6 +11,80 @@ CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP. ...@@ -11,6 +11,80 @@ CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
SECURITY: There are security fixes in the release. SECURITY: There are security fixes in the release.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
================================================================================
Redis 6.2.2 Released Mon April 19 19:00:00 IST 2021
================================================================================
Upgrade urgency: HIGH, if you're using ACL and pub/sub, CONFIG REWRITE, or
suffering from performance regression. see below.
Bug fixes for regressions in previous releases of Redis 6.2:
* Fix BGSAVE, AOFRW, and replication slowdown due to child reporting CoW (#8645)
* Fix short busy loop when timer event is about to fire (#8764)
* Fix default user, overwritten and reset users losing pubsub channel permissions (#8723)
* Fix config rewrite with an empty `save` config resulsing in default `save` values (#8719)
* Fix not starting on alpine/libmusl without IPv6 (#8655)
* Fix issues with propagation and MULTI/EXEC in modules (#8617)
Several issues around nested calls and thread safe contexts
Bug fixes that are only applicable to previous releases of Redis 6.2:
* ACL Pub/Sub channels permission handling for save/load scenario (#8794)
* Fix early rejection of PUBLISH inside MULTI-EXEC transaction (#8534)
* Fix missing SLOWLOG records for blocked commands (#8632)
* Allow RESET command during busy scripts (#8629)
* Fix some error replies were not counted on stats (#8659)
Bug fixes:
* Add a timeout mechanism for replicas stuck in fullsync (#8762)
* Process HELLO command even if the default user has no permissions (#8633)
* Client issuing a long running script and using a pipeline, got disconnected (#8715)
* Fix script kill to work also on scripts that use `pcall` (#8661)
* Fix list-compress-depth may compress more node than required (#8311)
* Fix redis-cli handling of rediss:// URL scheme (#8705)
* Cluster: Skip unnecessary check which may prevent failure detection (#8585)
* Cluster: Fix hang manual failover when replica just started (#8651)
* Sentinel: Fix info-refresh time field before sentinel get first response (#8567)
* Sentinel: Fix possible crash on failed connection attempt (#8627)
* Systemd: Send the readiness notification when a replica is ready to accept connections (#8409)
Command behavior changes:
* ZADD: fix wrong reply when INCR used with GT/LT which blocked the update (#8717)
It was responding with the incremented value rather than nil
* XAUTOCLAIM: fix response to return the next available id as the cursor (#8725)
Previous behavior was retuning the last one which was already scanned
* XAUTOCLAIM: fix JUSTID to prevent incrementing delivery_count (#8724)
New config options:
* Add cluster-allow-replica-migration config option (#5285)
* Add replica-announced config option (#8653)
* Add support for plaintext clients in TLS cluster (#8587)
* Add support for reading encrypted keyfiles (#8644)
Improvements:
* Fix performance regression in BRPOP on Redis 6.0 (#8689)
* Avoid adding slowlog entries for config with sensitive data (#8584)
* Improve redis-cli non-binary safe string handling (#8566)
* Optimize CLUSTER SLOTS reply (#8541)
* Handle remaining fsync errors (#8419)
Info fields and introspection changes:
* Strip % sign from current_fork_perc info field (#8628)
* Fix RSS memory info on FreeBSD (#8620)
* Fix client_recent_max_input/output_buffer in 'INFO CLIENTS' when all clients drop (#8588)
* Fix invalid master_link_down_since_seconds in info replication (#8785)
Platform and deployment-related changes:
* Fix FreeBSD <12.x builds (#8603)
Modules:
* Add macros for RedisModule_log logging levels (#4246)
* Add RedisModule_GetAbsExpire / RedisModule_SetAbsExpire (#8564)
* Add a module type for key space notification (#8759)
* Set module eviction context flag only in masters (#8631)
* Fix unusable RedisModule_IsAOFClient API (#8596)
* Fix missing EXEC on modules propagation after failed EVAL execution (#8654)
* Fix edge-case when a module client is unblocked (#8618)
================================================================================ ================================================================================
Redis 6.2.1 Released Mon Mar 1 17:51:36 IST 2021 Redis 6.2.1 Released Mon Mar 1 17:51:36 IST 2021
================================================================================ ================================================================================
......
...@@ -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,19 +294,19 @@ the structure definition. ...@@ -294,19 +294,19 @@ 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;
robj **argv; robj **argv;
redisDb *db; redisDb *db;
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) {
server.aof_fsync_offset = server.aof_current_size; serverLog(LL_WARNING,"Fail to fsync the AOF file: %s",strerror(errno));
server.aof_last_fsync = server.unixtime; } else {
server.aof_fsync_offset = server.aof_current_size;
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 ||
......
...@@ -31,13 +31,13 @@ const char *ascii_logo = ...@@ -31,13 +31,13 @@ const char *ascii_logo =
" _._ \n" " _._ \n"
" _.-``__ ''-._ \n" " _.-``__ ''-._ \n"
" _.-`` `. `_. ''-._ Redis %s (%s/%d) %s bit\n" " _.-`` `. `_. ''-._ Redis %s (%s/%d) %s bit\n"
" .-`` .-```. ```\\/ _.,_ ''-._ \n" " .-`` .-```. ```\\/ _.,_ ''-._ \n"
" ( ' , .-` | `, ) Running in %s mode\n" " ( ' , .-` | `, ) Running in %s mode\n"
" |`-._`-...-` __...-.``-._|'` _.-'| Port: %d\n" " |`-._`-...-` __...-.``-._|'` _.-'| Port: %d\n"
" | `-._ `._ / _.-' | 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;
static uint64_t cow_update_cost = 0;
static size_t cow = 0;
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 (cow) {
serverLog((info_type == CHILD_INFO_TYPE_CURRENT_INFO) ? LL_VERBOSE : LL_NOTICE,
"%s: %zu MB of memory used by copy-on-write",
pname, data.cow / (1024 * 1024));
}
}
data.information_type = info_type; data.information_type = info_type;
data.keys = keys; data.keys = keys;
data.cow = zmalloc_get_private_dirty(-1); data.cow = cow;
data.cow_updated = cow_updated;
data.progress = progress; data.progress = progress;
if (data.cow) {
serverLog((info_type == CHILD_INFO_TYPE_CURRENT_INFO) ? LL_VERBOSE : LL_NOTICE,
"%s: %zu MB of memory used by copy-on-write",
pname, data.cow/(1024*1024));
}
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);
} }
} }
This diff is collapsed.
...@@ -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. */ */
for (j = 0; j < server.saveparamslen; j++) { if (!server.saveparamslen) {
line = sdscatprintf(sdsempty(),"save %ld %d", rewriteConfigRewriteLine(state,"save",sdsnew("save \"\""),1);
(long) server.saveparams[j].seconds, server.saveparams[j].changes); } else {
rewriteConfigRewriteLine(state,"save",line,1); for (j = 0; j < server.saveparamslen; j++) {
line = sdscatprintf(sdsempty(),"save %ld %d",
(long) server.saveparams[j].seconds, server.saveparams[j].changes);
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
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