Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
2dba1e39
Unverified
Commit
2dba1e39
authored
Feb 01, 2021
by
Oran Agra
Committed by
GitHub
Feb 01, 2021
Browse files
Merge 6.2 RC3
parents
ec2d1807
95338f9c
Changes
91
Hide whitespace changes
Inline
Side-by-side
.github/workflows/ci.yml
View file @
2dba1e39
...
...
@@ -58,3 +58,14 @@ jobs:
run
:
|
yum -y install gcc make
make REDIS_CFLAGS='-Werror'
build-freebsd
:
runs-on
:
macos-latest
steps
:
-
uses
:
actions/checkout@v2
-
name
:
make
uses
:
vmactions/freebsd-vm@v0.1.0
with
:
usesh
:
true
prepare
:
pkg install -y gmake
run
:
gmake
.github/workflows/daily.yml
View file @
2dba1e39
...
...
@@ -99,6 +99,23 @@ jobs:
./runtest-cluster --tls
./runtest-cluster
test-ubuntu-io-threads
:
runs-on
:
ubuntu-latest
if
:
github.repository == 'redis/redis'
timeout-minutes
:
14400
steps
:
-
uses
:
actions/checkout@v2
-
name
:
make
run
:
|
make
-
name
:
test
run
:
|
sudo apt-get install tcl8.5 tcl-tls
./runtest --config io-threads 4 --config io-threads-do-reads yes --accurate --verbose --tags network
-
name
:
cluster tests
run
:
|
./runtest-cluster --config io-threads 4 --config io-threads-do-reads yes
test-valgrind
:
runs-on
:
ubuntu-latest
if
:
github.repository == 'redis/redis'
...
...
@@ -186,3 +203,20 @@ jobs:
-
name
:
cluster tests
run
:
./runtest-cluster
test-freebsd
:
runs-on
:
macos-latest
if
:
github.repository == 'redis/redis'
timeout-minutes
:
14400
steps
:
-
uses
:
actions/checkout@v2
-
name
:
test
uses
:
vmactions/freebsd-vm@v0.1.0
with
:
usesh
:
true
prepare
:
pkg install -y gmake lang/tcl85
run
:
|
gmake
./runtest --accurate --verbose --no-latency
MAKE=gmake ./runtest-moduleapi --verbose
./runtest-sentinel
./runtest-cluster
00-RELEASENOTES
View file @
2dba1e39
Redis 6.2 RC3 Released Tue Feb 1 14:00:00 IST 2021
================================================================================
Upgrade urgency LOW: This is the third Release Candidate of Redis 6.2.
Here is a comprehensive list of changes in this release compared to 6.2 RC2,
each one includes the PR number that added it, so you can get more details
at https://github.com/redis/redis/pull/<number>
New commands / args:
* Add HRANDFIELD and ZRANDMEMBER commands (#8297)
* Add FAILOVER command (#8315)
* Add GETEX, GETDEL commands (#8327)
* Add PXAT/EXAT arguments to SET command (#8327)
* Add SYNC arg to FLUSHALL and FLUSHDB, and ASYNC/SYNC arg to SCRIPT FLUSH (#8258)
Sentinel:
* Add hostname support to Sentinel (#8282)
* Prevent file descriptors from leaking into Sentinel scripts (#8242)
* Fix config file line order dependency and config rewrite sequence (#8271)
New configuration options:
* Add set-proc-title config option to disable changes to the process title (#3623)
* Add proc-title-template option to control what's shown in the process title (#8397)
* Add lazyfree-lazy-user-flush config option to control FLUSHALL, FLUSHDB and SCRIPT FLUSH (#8258)
Bug fixes:
* AOF: recover from last write error by turning on/off appendonly config (#8030)
* Exit on fsync error when the AOF fsync policy is 'always' (#8347)
* Avoid assertions (on older kernels) when testing arm64 CoW bug (#8405)
* CONFIG REWRITE should honor umask settings (#8371)
* Fix firstkey,lastkey,step in COMMAND command for some commands (#8367)
Special considerations:
* Fix misleading description of the save configuration directive (#8337)
Improvements:
* A way to get RDB file via replication without excessive replication buffers (#8303)
* Optimize performance of clusterGenNodesDescription for large clusters (#8182)
Info fields and introspection changes:
* SLOWLOG and LATENCY monitor include unblocking time of blocked commands (#7491)
Modules:
* Add modules API for streams (#8288)
* Add event for fork child birth and termination (#8289)
* Add RM_BlockedClientMeasureTime* etc, to track background processing in commandstats (#7491)
* Fix bug in v6.2, wrong value passed to the new unlink callback (#8381)
* Fix bug in v6.2, modules blocked on keys unblock on commands like LPUSH (#8356)
================================================================================
Redis 6.2 RC2 Released Tue Jan 12 16:17:20 IST 2021
================================================================================
...
...
@@ -255,35 +305,39 @@ and we don't get reports of serious issues for a while.
A special thank you for the amount of work put into this release by:
- Oran Agra
- Yossi Gottlieb
- Itamar Haber
- Guy Benoish
- Filipe Oliveira
- Viktor Söderqvist
- Guy Benoish
- Itamar Haber
- Yang Bodong
- Madelyn Olson
- Wang Yuan
- Felipe Machado
-
Yang Bodong
-
Wen Hui
- Tatsuya Arisawa
- Jonah H. Harris
- Raghav Muddur
- Jim Brunner
- Yaacov Hazan
-
Wen Hui
-
Allen Farris
- Chen Yang
- Nitai Caro
- Meir Shpilraien
- maohuazhu
- Valentino Geron
-
Qu Chen
-
Zhao Zhao
- sundb
- Qu Chen
- George Prekas
- Zhao Zhao
- Tyson Andre
- Michael Grunder
- alexronke-channeladvisor
- Andy Pan
- Wu Yunlong
- Wei Kukey
- Yoav Steinberg
- Uri Shachar
- Greg Femec
- Uri Shachar
- Nykolas Laurentino de Lima
- xhe
- zhenwei pi
...
...
redis.conf
View file @
2dba1e39
...
...
@@ -325,31 +325,52 @@ databases 16
# ASCII art logo in startup logs by setting the following option to yes.
always
-
show
-
logo
no
# By default, Redis modifies the process title (as seen in 'top' and 'ps') to
# provide some runtime information. It is possible to disable this and leave
# the process name as executed by setting the following to no.
set
-
proc
-
title
yes
# When changing the process title, Redis uses the following template to construct
# the modified title.
#
# Template variables are specified in curly brackets. The following variables are
# supported:
#
# {title} Name of process as executed if parent, or type of child process.
# {listen-addr} Bind address or '*' followed by TCP or TLS port listening on, or
# Unix socket if only that's available.
# {server-mode} Special mode, i.e. "[sentinel]" or "[cluster]".
# {port} TCP port listening on, or 0.
# {tls-port} TLS port listening on, or 0.
# {unixsocket} Unix domain socket listening on, or "".
# {config-file} Name of configuration file used.
#
proc
-
title
-
template
"{title} {listen-addr} {server-mode}"
################################ SNAPSHOTTING ################################
# Save the DB to disk.
#
#
S
ave
the DB on disk:
#
s
ave
<seconds> <changes>
#
# save <seconds> <changes>
# Redis will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
#
Will save the DB if both the given number of seconds and the given
#
number of write operations against the DB occurred.
#
Snapshotting can be completely disabled with a single empty string argument
#
as in following example:
#
# In the example below the behavior will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
# save ""
#
# Note: you can disable saving completely by commenting out all "save" lines.
# Unless specified otherwise, by default Redis will save the DB:
# * After 3600 seconds (an hour) if at least 1 key changed
# * After 300 seconds (5 minutes) if at least 100 keys changed
# * After 60 seconds if at least 10000 keys changed
#
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
# like in the following example:
# You can set these explicitly by uncommenting the three following lines.
#
# save ""
save
900
1
save
300
10
save
60
10000
# save 3600 1
# save 300 100
# save 60 10000
# By default Redis will stop accepting writes if RDB snapshots are enabled
# (at least one save point) and the latest background save failed.
...
...
@@ -1089,6 +1110,13 @@ replica-lazy-flush no
lazyfree
-
lazy
-
user
-
del
no
# FLUSHDB, FLUSHALL, and SCRIPT FLUSH support both asynchronous and synchronous
# deletion, which can be controlled by passing the [SYNC|ASYNC] flags into the
# commands. When neither flag is passed, this directive will be used to determine
# if the data should be deleted asynchronously.
lazyfree
-
lazy
-
user
-
flush
no
################################ THREADED I/O #################################
# Redis is mostly single threaded, however there are certain threaded
...
...
runtest-moduleapi
View file @
2dba1e39
...
...
@@ -23,6 +23,7 @@ $TCLSH tests/test_helper.tcl \
--single
unit/moduleapi/hooks
\
--single
unit/moduleapi/misc
\
--single
unit/moduleapi/blockonkeys
\
--single
unit/moduleapi/blockonbackground
\
--single
unit/moduleapi/scan
\
--single
unit/moduleapi/datatype
\
--single
unit/moduleapi/auth
\
...
...
@@ -31,4 +32,5 @@ $TCLSH tests/test_helper.tcl \
--single
unit/moduleapi/getkeys
\
--single
unit/moduleapi/test_lazyfree
\
--single
unit/moduleapi/defrag
\
--single
unit/moduleapi/stream
\
"
${
@
}
"
sentinel.conf
View file @
2dba1e39
...
...
@@ -321,3 +321,21 @@ sentinel deny-scripts-reconfig yes
# is possible to just rename a command to itself:
#
# SENTINEL rename-command mymaster CONFIG CONFIG
# HOSTNAMES SUPPORT
#
# Normally Sentinel uses only IP addresses and requires SENTINEL MONITOR
# to specify an IP address. Also, it requires the Redis replica-announce-ip
# keyword to specify only IP addresses.
#
# You may enable hostnames support by enabling resolve-hostnames. Note
# that you must make sure your DNS is configured properly and that DNS
# resolution does not introduce very long delays.
#
SENTINEL
resolve
-
hostnames
no
# When resolve-hostnames is enabled, Sentinel still uses IP addresses
# when exposing instances to users, configuration files, etc. If you want
# to retain the hostnames when announced, enable announce-hostnames below.
#
SENTINEL
announce
-
hostnames
no
src/acl.c
View file @
2dba1e39
...
...
@@ -1024,8 +1024,8 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) {
/* Return a description of the error that occurred in ACLSetUser() according to
* the errno value set by the function on error. */
char
*
ACLSetUserStringError
(
void
)
{
char
*
errmsg
=
"Wrong format"
;
const
char
*
ACLSetUserStringError
(
void
)
{
const
char
*
errmsg
=
"Wrong format"
;
if
(
errno
==
ENOENT
)
errmsg
=
"Unknown command or category name in ACL"
;
else
if
(
errno
==
EINVAL
)
...
...
@@ -1454,7 +1454,7 @@ int ACLLoadConfiguredUsers(void) {
/* Load every rule defined for this user. */
for
(
int
j
=
1
;
aclrules
[
j
];
j
++
)
{
if
(
ACLSetUser
(
u
,
aclrules
[
j
],
sdslen
(
aclrules
[
j
]))
!=
C_OK
)
{
char
*
errmsg
=
ACLSetUserStringError
();
const
char
*
errmsg
=
ACLSetUserStringError
();
serverLog
(
LL_WARNING
,
"Error loading ACL rule '%s' for "
"the user named '%s': %s"
,
aclrules
[
j
],
aclrules
[
0
],
errmsg
);
...
...
@@ -1587,7 +1587,7 @@ sds ACLLoadFromFile(const char *filename) {
for
(
j
=
2
;
j
<
argc
;
j
++
)
{
argv
[
j
]
=
sdstrim
(
argv
[
j
],
"
\t\r\n
"
);
if
(
ACLSetUser
(
fakeuser
,
argv
[
j
],
sdslen
(
argv
[
j
]))
!=
C_OK
)
{
char
*
errmsg
=
ACLSetUserStringError
();
const
char
*
errmsg
=
ACLSetUserStringError
();
errors
=
sdscatprintf
(
errors
,
"%s:%d: %s. "
,
server
.
acl_filename
,
linenum
,
errmsg
);
...
...
@@ -1908,7 +1908,7 @@ void aclCommand(client *c) {
for
(
int
j
=
3
;
j
<
c
->
argc
;
j
++
)
{
if
(
ACLSetUser
(
tempu
,
c
->
argv
[
j
]
->
ptr
,
sdslen
(
c
->
argv
[
j
]
->
ptr
))
!=
C_OK
)
{
char
*
errmsg
=
ACLSetUserStringError
();
const
char
*
errmsg
=
ACLSetUserStringError
();
addReplyErrorFormat
(
c
,
"Error in ACL SETUSER modifier '%s': %s"
,
(
char
*
)
c
->
argv
[
j
]
->
ptr
,
errmsg
);
...
...
src/ae.c
View file @
2dba1e39
...
...
@@ -31,6 +31,7 @@
*/
#include "ae.h"
#include "anet.h"
#include <stdio.h>
#include <sys/time.h>
...
...
src/ae_epoll.c
View file @
2dba1e39
...
...
@@ -51,6 +51,7 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
zfree
(
state
);
return
-
1
;
}
anetCloexec
(
state
->
epfd
);
eventLoop
->
apidata
=
state
;
return
0
;
}
...
...
src/ae_evport.c
View file @
2dba1e39
...
...
@@ -82,6 +82,7 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
zfree
(
state
);
return
-
1
;
}
anetCloexec
(
state
->
portfd
);
state
->
npending
=
0
;
...
...
src/ae_kqueue.c
View file @
2dba1e39
...
...
@@ -53,6 +53,7 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
zfree
(
state
);
return
-
1
;
}
anetCloexec
(
state
->
kqfd
);
eventLoop
->
apidata
=
state
;
return
0
;
}
...
...
src/anet.c
View file @
2dba1e39
...
...
@@ -69,6 +69,11 @@ int anetSetBlock(char *err, int fd, int non_block) {
return
ANET_ERR
;
}
/* Check if this flag has been set or unset, if so,
* then there is no need to call fcntl to set/unset it again. */
if
(
!!
(
flags
&
O_NONBLOCK
)
==
!!
non_block
)
return
ANET_OK
;
if
(
non_block
)
flags
|=
O_NONBLOCK
;
else
...
...
@@ -89,6 +94,29 @@ int anetBlock(char *err, int fd) {
return
anetSetBlock
(
err
,
fd
,
0
);
}
/* Enable the FD_CLOEXEC on the given fd to avoid fd leaks.
* This function should be invoked for fd's on specific places
* where fork + execve system calls are called. */
int
anetCloexec
(
int
fd
)
{
int
r
;
int
flags
;
do
{
r
=
fcntl
(
fd
,
F_GETFD
);
}
while
(
r
==
-
1
&&
errno
==
EINTR
);
if
(
r
==
-
1
||
(
r
&
FD_CLOEXEC
))
return
r
;
flags
=
r
|
FD_CLOEXEC
;
do
{
r
=
fcntl
(
fd
,
F_SETFD
,
flags
);
}
while
(
r
==
-
1
&&
errno
==
EINTR
);
return
r
;
}
/* Set TCP keep alive option to detect dead peers. The interval option
* is only used for Linux as we are using Linux-specific APIs to set
* the probe send time, interval, and count. */
...
...
@@ -207,14 +235,13 @@ int anetRecvTimeout(char *err, int fd, long long ms) {
return
ANET_OK
;
}
/* anetGenericResolve() is called by anetResolve() and anetResolveIP() to
* do the actual work. It resolves the hostname "host" and set the string
* representation of the IP address into the buffer pointed by "ipbuf".
/* Resolve the hostname "host" and set the string representation of the
* IP address into the buffer pointed by "ipbuf".
*
* If flags is set to ANET_IP_ONLY the function only resolves hostnames
* that are actually already IPv4 or IPv6 addresses. This turns the function
* into a validating / normalizing function. */
int
anet
Generic
Resolve
(
char
*
err
,
char
*
host
,
char
*
ipbuf
,
size_t
ipbuf_len
,
int
anetResolve
(
char
*
err
,
char
*
host
,
char
*
ipbuf
,
size_t
ipbuf_len
,
int
flags
)
{
struct
addrinfo
hints
,
*
info
;
...
...
@@ -241,14 +268,6 @@ int anetGenericResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,
return
ANET_OK
;
}
int
anetResolve
(
char
*
err
,
char
*
host
,
char
*
ipbuf
,
size_t
ipbuf_len
)
{
return
anetGenericResolve
(
err
,
host
,
ipbuf
,
ipbuf_len
,
ANET_NONE
);
}
int
anetResolveIP
(
char
*
err
,
char
*
host
,
char
*
ipbuf
,
size_t
ipbuf_len
)
{
return
anetGenericResolve
(
err
,
host
,
ipbuf
,
ipbuf_len
,
ANET_IP_ONLY
);
}
static
int
anetSetReuseAddr
(
char
*
err
,
int
fd
)
{
int
yes
=
1
;
/* Make sure connection-intensive things like the redis benchmark
...
...
src/anet.h
View file @
2dba1e39
...
...
@@ -60,8 +60,7 @@ int anetTcpNonBlockBestEffortBindConnect(char *err, const char *addr, int port,
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
anetResolveIP
(
char
*
err
,
char
*
host
,
char
*
ipbuf
,
size_t
ipbuf_len
);
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
anetTcp6Server
(
char
*
err
,
int
port
,
char
*
bindaddr
,
int
backlog
);
int
anetUnixServer
(
char
*
err
,
char
*
path
,
mode_t
perm
,
int
backlog
);
...
...
@@ -70,6 +69,7 @@ int anetUnixAccept(char *err, int serversock);
int
anetWrite
(
int
fd
,
char
*
buf
,
int
count
);
int
anetNonBlock
(
char
*
err
,
int
fd
);
int
anetBlock
(
char
*
err
,
int
fd
);
int
anetCloexec
(
int
fd
);
int
anetEnableTcpNoDelay
(
char
*
err
,
int
fd
);
int
anetDisableTcpNoDelay
(
char
*
err
,
int
fd
);
int
anetTcpKeepAlive
(
char
*
err
,
int
fd
);
...
...
src/aof.c
View file @
2dba1e39
...
...
@@ -235,6 +235,8 @@ void stopAppendOnly(void) {
serverAssert
(
server
.
aof_state
!=
AOF_OFF
);
flushAppendOnlyFile
(
1
);
redis_fsync
(
server
.
aof_fd
);
server
.
aof_fsync_offset
=
server
.
aof_current_size
;
server
.
aof_last_fsync
=
server
.
unixtime
;
close
(
server
.
aof_fd
);
server
.
aof_fd
=
-
1
;
...
...
@@ -242,6 +244,8 @@ void stopAppendOnly(void) {
server
.
aof_state
=
AOF_OFF
;
server
.
aof_rewrite_scheduled
=
0
;
killAppendOnlyChild
();
sdsfree
(
server
.
aof_buf
);
server
.
aof_buf
=
sdsempty
();
}
/* Called when the user switches from "appendonly no" to "appendonly yes"
...
...
@@ -285,6 +289,12 @@ int startAppendOnly(void) {
server
.
aof_state
=
AOF_WAIT_REWRITE
;
server
.
aof_last_fsync
=
server
.
unixtime
;
server
.
aof_fd
=
newfd
;
/* If AOF was in error state, we just ignore it and log the event. */
if
(
server
.
aof_last_write_status
==
C_ERR
)
{
serverLog
(
LL_WARNING
,
"AOF reopen, just ignore the last error."
);
server
.
aof_last_write_status
=
C_OK
;
}
return
C_OK
;
}
...
...
@@ -451,10 +461,11 @@ void flushAppendOnlyFile(int force) {
/* Handle the AOF write error. */
if
(
server
.
aof_fsync
==
AOF_FSYNC_ALWAYS
)
{
/* We can't recover when the fsync policy is ALWAYS since the
* reply for the client is already in the output buffers, and we
* have the contract with the user that on acknowledged write data
* is synced on disk. */
/* We can't recover when the fsync policy is ALWAYS since the reply
* for the client is already in the output buffers (both writes and
* reads), and the changes to the db can't be rolled back. Since we
* have a contract with the user that on acknowledged or observed
* writes are is synced on disk, we must exit. */
serverLog
(
LL_WARNING
,
"Can't recover from AOF write error when the AOF fsync policy is 'always'. Exiting..."
);
exit
(
1
);
}
else
{
...
...
@@ -502,7 +513,14 @@ try_fsync:
/* redis_fsync is defined as fdatasync() for Linux in order to avoid
* flushing metadata. */
latencyStartMonitor
(
latency
);
redis_fsync
(
server
.
aof_fd
);
/* Let's try to get this data on the disk */
/* Let's try to get this data on the disk. To guarantee data safe when
* the AOF fsync policy is 'always', we should exit if failed to fsync
* AOF (see comment next to the exit(1) after write error above). */
if
(
redis_fsync
(
server
.
aof_fd
)
==
-
1
)
{
serverLog
(
LL_WARNING
,
"Can't persist AOF for fsync error when the "
"AOF fsync policy is 'always': %s. Exiting..."
,
strerror
(
errno
));
exit
(
1
);
}
latencyEndMonitor
(
latency
);
latencyAddSampleIfNeeded
(
"aof-fsync-always"
,
latency
);
server
.
aof_fsync_offset
=
server
.
aof_current_size
;
...
...
@@ -581,8 +599,6 @@ sds catAppendOnlyExpireAtCommand(sds buf, struct redisCommand *cmd, robj *key, r
void
feedAppendOnlyFile
(
struct
redisCommand
*
cmd
,
int
dictid
,
robj
**
argv
,
int
argc
)
{
sds
buf
=
sdsempty
();
robj
*
tmpargv
[
3
];
/* The DB this command was targeting is not the same as the last command
* we appended. To issue a SELECT command is needed. */
if
(
dictid
!=
server
.
aof_selected_db
)
{
...
...
@@ -598,32 +614,31 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a
cmd
->
proc
==
expireatCommand
)
{
/* Translate EXPIRE/PEXPIRE/EXPIREAT into PEXPIREAT */
buf
=
catAppendOnlyExpireAtCommand
(
buf
,
cmd
,
argv
[
1
],
argv
[
2
]);
}
else
if
(
cmd
->
proc
==
setexCommand
||
cmd
->
proc
==
psetexCommand
)
{
/* Translate SETEX/PSETEX to SET and PEXPIREAT */
tmpargv
[
0
]
=
createStringObject
(
"SET"
,
3
);
tmpargv
[
1
]
=
argv
[
1
];
tmpargv
[
2
]
=
argv
[
3
];
buf
=
catAppendOnlyGenericCommand
(
buf
,
3
,
tmpargv
);
decrRefCount
(
tmpargv
[
0
]);
buf
=
catAppendOnlyExpireAtCommand
(
buf
,
cmd
,
argv
[
1
],
argv
[
2
]);
}
else
if
(
cmd
->
proc
==
setCommand
&&
argc
>
3
)
{
int
i
;
robj
*
exarg
=
NULL
,
*
pxarg
=
NULL
;
for
(
i
=
3
;
i
<
argc
;
i
++
)
{
if
(
!
strcasecmp
(
argv
[
i
]
->
ptr
,
"
e
x"
))
exarg
=
argv
[
i
+
1
];
if
(
!
strcasecmp
(
argv
[
i
]
->
ptr
,
"px"
))
pxarg
=
argv
[
i
+
1
];
robj
*
pxarg
=
NULL
;
/* When SET is used with EX/PX argument setGenericCommand propagates them with PX millisecond argument.
* So since the command arguments are re-written there, we can rely here on the index of PX being 3. */
if
(
!
strcasecmp
(
argv
[
3
]
->
ptr
,
"
p
x"
))
{
pxarg
=
argv
[
4
];
}
serverAssert
(
!
(
exarg
&&
pxarg
));
if
(
exarg
||
pxarg
)
{
/* Translate SET [EX seconds][PX milliseconds] to SET and PEXPIREAT */
buf
=
catAppendOnlyGenericCommand
(
buf
,
3
,
argv
);
if
(
exarg
)
buf
=
catAppendOnlyExpireAtCommand
(
buf
,
server
.
expireCommand
,
argv
[
1
],
exarg
);
if
(
pxarg
)
buf
=
catAppendOnlyExpireAtCommand
(
buf
,
server
.
pexpireCommand
,
argv
[
1
],
pxarg
);
/* For AOF we convert SET key value relative time in milliseconds to SET key value absolute time in
* millisecond. Whenever the condition is true it implies that original SET has been transformed
* to SET PX with millisecond time argument so we do not need to worry about unit here.*/
if
(
pxarg
)
{
robj
*
millisecond
=
getDecodedObject
(
pxarg
);
long
long
when
=
strtoll
(
millisecond
->
ptr
,
NULL
,
10
);
when
+=
mstime
();
decrRefCount
(
millisecond
);
robj
*
newargs
[
5
];
newargs
[
0
]
=
argv
[
0
];
newargs
[
1
]
=
argv
[
1
];
newargs
[
2
]
=
argv
[
2
];
newargs
[
3
]
=
shared
.
pxat
;
newargs
[
4
]
=
createStringObjectFromLongLong
(
when
);
buf
=
catAppendOnlyGenericCommand
(
buf
,
5
,
newargs
);
decrRefCount
(
newargs
[
4
]);
}
else
{
buf
=
catAppendOnlyGenericCommand
(
buf
,
argc
,
argv
);
}
...
...
@@ -1852,6 +1867,20 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
}
latencyEndMonitor
(
latency
);
latencyAddSampleIfNeeded
(
"aof-rewrite-diff-write"
,
latency
);
if
(
server
.
aof_fsync
==
AOF_FSYNC_EVERYSEC
)
{
aof_background_fsync
(
newfd
);
}
else
if
(
server
.
aof_fsync
==
AOF_FSYNC_ALWAYS
)
{
latencyStartMonitor
(
latency
);
if
(
redis_fsync
(
newfd
)
==
-
1
)
{
serverLog
(
LL_WARNING
,
"Error trying to fsync the parent diff to the rewritten AOF: %s"
,
strerror
(
errno
));
close
(
newfd
);
goto
cleanup
;
}
latencyEndMonitor
(
latency
);
latencyAddSampleIfNeeded
(
"aof-rewrite-done-fsync"
,
latency
);
}
serverLog
(
LL_NOTICE
,
"Residual parent diff successfully flushed to the rewritten AOF (%.2f MB)"
,
(
double
)
aofRewriteBufferSize
()
/
(
1024
*
1024
));
...
...
@@ -1919,14 +1948,11 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
/* AOF enabled, replace the old fd with the new one. */
oldfd
=
server
.
aof_fd
;
server
.
aof_fd
=
newfd
;
if
(
server
.
aof_fsync
==
AOF_FSYNC_ALWAYS
)
redis_fsync
(
newfd
);
else
if
(
server
.
aof_fsync
==
AOF_FSYNC_EVERYSEC
)
aof_background_fsync
(
newfd
);
server
.
aof_selected_db
=
-
1
;
/* Make sure SELECT is re-issued */
aofUpdateCurrentSize
();
server
.
aof_rewrite_base_size
=
server
.
aof_current_size
;
server
.
aof_fsync_offset
=
server
.
aof_current_size
;
server
.
aof_last_fsync
=
server
.
unixtime
;
/* Clear regular AOF buffer since its contents was just written to
* the new AOF from the background rewrite buffer. */
...
...
src/blocked.c
View file @
2dba1e39
...
...
@@ -61,6 +61,9 @@
*/
#include "server.h"
#include "slowlog.h"
#include "latency.h"
#include "monotonic.h"
int
serveClientBlockedOnList
(
client
*
receiver
,
robj
*
key
,
robj
*
dstkey
,
redisDb
*
db
,
robj
*
value
,
int
wherefrom
,
int
whereto
);
int
getListPositionFromObjectOrReply
(
client
*
c
,
robj
*
arg
,
int
*
position
);
...
...
@@ -97,6 +100,20 @@ void blockClient(client *c, int btype) {
}
}
/* This function is called after a client has finished a blocking operation
* in order to update the total command duration, log the command into
* the Slow log if needed, and log the reply duration event if needed. */
void
updateStatsOnUnblock
(
client
*
c
,
long
blocked_us
,
long
reply_us
){
const
ustime_t
total_cmd_duration
=
c
->
duration
+
blocked_us
+
reply_us
;
c
->
lastcmd
->
microseconds
+=
total_cmd_duration
;
/* Log the command into the Slow log if needed. */
if
(
!
(
c
->
lastcmd
->
flags
&
CMD_SKIP_SLOWLOG
))
{
slowlogPushEntryIfNeeded
(
c
,
c
->
argv
,
c
->
argc
,
total_cmd_duration
);
/* Log the reply duration event. */
latencyAddSampleIfNeeded
(
"command-unblocking"
,
reply_us
/
1000
);
}
}
/* This function is called in the beforeSleep() function of the event loop
* in order to process the pending input buffer of clients that were
* unblocked after a blocking operation. */
...
...
@@ -264,6 +281,8 @@ void serveClientsBlockedOnListKey(robj *o, readyList *rl) {
if
(
dstkey
)
incrRefCount
(
dstkey
);
unblockClient
(
receiver
);
monotime
replyTimer
;
elapsedStart
(
&
replyTimer
);
if
(
serveClientBlockedOnList
(
receiver
,
rl
->
key
,
dstkey
,
rl
->
db
,
value
,
wherefrom
,
whereto
)
==
C_ERR
)
...
...
@@ -272,6 +291,7 @@ void serveClientsBlockedOnListKey(robj *o, readyList *rl) {
* to also undo the POP operation. */
listTypePush
(
o
,
value
,
wherefrom
);
}
updateStatsOnUnblock
(
receiver
,
0
,
elapsedUs
(
replyTimer
));
if
(
dstkey
)
decrRefCount
(
dstkey
);
decrRefCount
(
value
);
...
...
@@ -316,7 +336,10 @@ void serveClientsBlockedOnSortedSetKey(robj *o, readyList *rl) {
receiver
->
lastcmd
->
proc
==
bzpopminCommand
)
?
ZSET_MIN
:
ZSET_MAX
;
unblockClient
(
receiver
);
monotime
replyTimer
;
elapsedStart
(
&
replyTimer
);
genericZpopCommand
(
receiver
,
&
rl
->
key
,
1
,
where
,
1
,
NULL
);
updateStatsOnUnblock
(
receiver
,
0
,
elapsedUs
(
replyTimer
));
zcard
--
;
/* Replicate the command. */
...
...
@@ -406,6 +429,8 @@ void serveClientsBlockedOnStreamKey(robj *o, readyList *rl) {
}
}
monotime
replyTimer
;
elapsedStart
(
&
replyTimer
);
/* Emit the two elements sub-array consisting of
* the name of the stream and the data we
* extracted from it. Wrapped in a single-item
...
...
@@ -425,6 +450,7 @@ void serveClientsBlockedOnStreamKey(robj *o, readyList *rl) {
streamReplyWithRange
(
receiver
,
s
,
&
start
,
NULL
,
receiver
->
bpop
.
xread_count
,
0
,
group
,
consumer
,
noack
,
&
pi
);
updateStatsOnUnblock
(
receiver
,
0
,
elapsedUs
(
replyTimer
));
/* Note that after we unblock the client, 'gt'
* and other receiver->bpop stuff are no longer
...
...
@@ -471,7 +497,10 @@ void serveClientsBlockedOnKeyByModule(readyList *rl) {
* different modules with different triggers to consider if a key
* is ready or not. This means we can't exit the loop but need
* to continue after the first failure. */
monotime
replyTimer
;
elapsedStart
(
&
replyTimer
);
if
(
!
moduleTryServeClientBlockedOnKey
(
receiver
,
rl
->
key
))
continue
;
updateStatsOnUnblock
(
receiver
,
0
,
elapsedUs
(
replyTimer
));
moduleUnblockClient
(
receiver
);
}
...
...
@@ -684,10 +713,20 @@ static int getBlockedTypeByType(int type) {
void
signalKeyAsReady
(
redisDb
*
db
,
robj
*
key
,
int
type
)
{
readyList
*
rl
;
/*
If no clients are blocked on this type, just
return */
/*
Quick
return
s.
*/
int
btype
=
getBlockedTypeByType
(
type
);
if
(
btype
==
BLOCKED_NONE
||
!
server
.
blocked_clients_by_type
[
btype
])
if
(
btype
==
BLOCKED_NONE
)
{
/* The type can never block. */
return
;
}
if
(
!
server
.
blocked_clients_by_type
[
btype
]
&&
!
server
.
blocked_clients_by_type
[
BLOCKED_MODULE
])
{
/* No clients block on this type. Note: Blocked modules are represented
* by BLOCKED_MODULE, even if the intention is to wake up by normal
* types (list, zset, stream), so we need to check that there are no
* blocked modules before we do a quick return here. */
return
;
}
/* No clients blocking for this key? No need to queue it. */
if
(
dictFind
(
db
->
blocking_keys
,
key
)
==
NULL
)
return
;
...
...
src/cluster.c
View file @
2dba1e39
...
...
@@ -398,7 +398,7 @@ int clusterLockConfig(char *filename) {
/* To lock it, we need to open the file in a way it is created if
* it does not exist, otherwise there is a race condition with other
* processes. */
int
fd
=
open
(
filename
,
O_WRONLY
|
O_CREAT
,
0644
);
int
fd
=
open
(
filename
,
O_WRONLY
|
O_CREAT
|
O_CLOEXEC
,
0644
);
if
(
fd
==
-
1
)
{
serverLog
(
LL_WARNING
,
"Can't open %s in order to acquire a lock: %s"
,
...
...
@@ -509,8 +509,7 @@ void clusterInit(void) {
serverLog
(
LL_WARNING
,
"Redis port number too high. "
"Cluster communication port is 10,000 port "
"numbers higher than your Redis port. "
"Your Redis port number must be "
"lower than 55535."
);
"Your Redis port number must be 55535 or less."
);
exit
(
1
);
}
if
(
listenToPort
(
port
+
CLUSTER_PORT_INCR
,
...
...
@@ -779,6 +778,7 @@ clusterNode *createClusterNode(char *nodename, int flags) {
node
->
configEpoch
=
0
;
node
->
flags
=
flags
;
memset
(
node
->
slots
,
0
,
sizeof
(
node
->
slots
));
node
->
slots_info
=
NULL
;
node
->
numslots
=
0
;
node
->
numslaves
=
0
;
node
->
slaves
=
NULL
;
...
...
@@ -4144,8 +4144,8 @@ sds clusterGenNodeDescription(clusterNode *node) {
sds
ci
;
/* Node coordinates */
ci
=
sdscat
printf
(
sdsempty
(),
"%.40s %s:%d@%d "
,
node
->
name
,
ci
=
sdscat
len
(
sdsempty
(),
node
->
name
,
CLUSTER_NAMELEN
);
ci
=
sdscatfmt
(
ci
,
" %s:%i@%i "
,
node
->
ip
,
node
->
port
,
node
->
cport
);
...
...
@@ -4154,40 +4154,46 @@ sds clusterGenNodeDescription(clusterNode *node) {
ci
=
representClusterNodeFlags
(
ci
,
node
->
flags
);
/* Slave of... or just "-" */
ci
=
sdscatlen
(
ci
,
" "
,
1
);
if
(
node
->
slaveof
)
ci
=
sdscat
printf
(
ci
,
" %.40s "
,
node
->
slaveof
->
name
);
ci
=
sdscat
len
(
ci
,
node
->
slaveof
->
name
,
CLUSTER_NAMELEN
);
else
ci
=
sdscatlen
(
ci
,
"
-
"
,
3
);
ci
=
sdscatlen
(
ci
,
"
-
"
,
1
);
unsigned
long
long
nodeEpoch
=
node
->
configEpoch
;
if
(
nodeIsSlave
(
node
)
&&
node
->
slaveof
)
{
nodeEpoch
=
node
->
slaveof
->
configEpoch
;
}
/* Latency from the POV of this node, config epoch, link status */
ci
=
sdscat
printf
(
ci
,
"%lld %lld %llu
%s"
,
ci
=
sdscat
fmt
(
ci
,
" %I %I %U
%s"
,
(
long
long
)
node
->
ping_sent
,
(
long
long
)
node
->
pong_received
,
nodeEpoch
,
(
node
->
link
||
node
->
flags
&
CLUSTER_NODE_MYSELF
)
?
"connected"
:
"disconnected"
);
/* Slots served by this instance */
start
=
-
1
;
for
(
j
=
0
;
j
<
CLUSTER_SLOTS
;
j
++
)
{
int
bit
;
/* Slots served by this instance. If we already have slots info,
* append it diretly, otherwise, generate slots only if it has. */
if
(
node
->
slots_info
)
{
ci
=
sdscatsds
(
ci
,
node
->
slots_info
);
}
else
if
(
node
->
numslots
>
0
)
{
start
=
-
1
;
for
(
j
=
0
;
j
<
CLUSTER_SLOTS
;
j
++
)
{
int
bit
;
if
((
bit
=
clusterNodeGetSlotBit
(
node
,
j
))
!=
0
)
{
if
(
start
==
-
1
)
start
=
j
;
}
if
(
start
!=
-
1
&&
(
!
bit
||
j
==
CLUSTER_SLOTS
-
1
))
{
if
(
bit
&&
j
==
CLUSTER_SLOTS
-
1
)
j
++
;
if
((
bit
=
clusterNodeGetSlotBit
(
node
,
j
))
!=
0
)
{
if
(
start
==
-
1
)
start
=
j
;
}
if
(
start
!=
-
1
&&
(
!
bit
||
j
==
CLUSTER_SLOTS
-
1
))
{
if
(
bit
&&
j
==
CLUSTER_SLOTS
-
1
)
j
++
;
if
(
start
==
j
-
1
)
{
ci
=
sdscatprintf
(
ci
,
" %d"
,
start
);
}
else
{
ci
=
sdscatprintf
(
ci
,
" %d-%d"
,
start
,
j
-
1
);
if
(
start
==
j
-
1
)
{
ci
=
sdscatfmt
(
ci
,
" %i"
,
start
);
}
else
{
ci
=
sdscatfmt
(
ci
,
" %i-%i"
,
start
,
j
-
1
);
}
start
=
-
1
;
}
start
=
-
1
;
}
}
...
...
@@ -4208,6 +4214,41 @@ sds clusterGenNodeDescription(clusterNode *node) {
return
ci
;
}
/* Generate the slot topology for all nodes and store the string representation
* in the slots_info struct on the node. This is used to improve the efficiency
* of clusterGenNodesDescription() because it removes looping of the slot space
* for generating the slot info for each node individually. */
void
clusterGenNodesSlotsInfo
(
int
filter
)
{
clusterNode
*
n
=
NULL
;
int
start
=
-
1
;
for
(
int
i
=
0
;
i
<=
CLUSTER_SLOTS
;
i
++
)
{
/* Find start node and slot id. */
if
(
n
==
NULL
)
{
if
(
i
==
CLUSTER_SLOTS
)
break
;
n
=
server
.
cluster
->
slots
[
i
];
start
=
i
;
continue
;
}
/* Generate slots info when occur different node with start
* or end of slot. */
if
(
i
==
CLUSTER_SLOTS
||
n
!=
server
.
cluster
->
slots
[
i
])
{
if
(
!
(
n
->
flags
&
filter
))
{
if
(
n
->
slots_info
==
NULL
)
n
->
slots_info
=
sdsempty
();
if
(
start
==
i
-
1
)
{
n
->
slots_info
=
sdscatfmt
(
n
->
slots_info
,
" %i"
,
start
);
}
else
{
n
->
slots_info
=
sdscatfmt
(
n
->
slots_info
,
" %i-%i"
,
start
,
i
-
1
);
}
}
if
(
i
==
CLUSTER_SLOTS
)
break
;
n
=
server
.
cluster
->
slots
[
i
];
start
=
i
;
}
}
}
/* Generate a csv-alike representation of the nodes we are aware of,
* including the "myself" node, and return an SDS string containing the
* representation (it is up to the caller to free it).
...
...
@@ -4225,6 +4266,9 @@ sds clusterGenNodesDescription(int filter) {
dictIterator
*
di
;
dictEntry
*
de
;
/* Generate all nodes slots info firstly. */
clusterGenNodesSlotsInfo
(
filter
);
di
=
dictGetSafeIterator
(
server
.
cluster
->
nodes
);
while
((
de
=
dictNext
(
di
))
!=
NULL
)
{
clusterNode
*
node
=
dictGetVal
(
de
);
...
...
@@ -4234,6 +4278,12 @@ sds clusterGenNodesDescription(int filter) {
ci
=
sdscatsds
(
ci
,
ni
);
sdsfree
(
ni
);
ci
=
sdscatlen
(
ci
,
"
\n
"
,
1
);
/* Release slots info. */
if
(
node
->
slots_info
)
{
sdsfree
(
node
->
slots_info
);
node
->
slots_info
=
NULL
;
}
}
dictReleaseIterator
(
di
);
return
ci
;
...
...
src/cluster.h
View file @
2dba1e39
...
...
@@ -118,6 +118,7 @@ typedef struct clusterNode {
int
flags
;
/* CLUSTER_NODE_... */
uint64_t
configEpoch
;
/* Last configEpoch observed for this node */
unsigned
char
slots
[
CLUSTER_SLOTS
/
8
];
/* slots handled by this node */
sds
slots_info
;
/* Slots info represented by string. */
int
numslots
;
/* Number of slots handled by this node */
int
numslaves
;
/* Number of slave nodes, if this is a master */
struct
clusterNode
**
slaves
;
/* pointers to slave nodes */
...
...
src/config.c
View file @
2dba1e39
...
...
@@ -153,15 +153,15 @@ int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT] = { 0, 200, 800 };
typedef
struct
boolConfigData
{
int
*
config
;
/* The pointer to the server config this value is stored in */
const
int
default_value
;
/* The default value of the config on rewrite */
int
(
*
is_valid_fn
)(
int
val
,
char
**
err
);
/* Optional function to check validity of new value (generic doc above) */
int
(
*
update_fn
)(
int
val
,
int
prev
,
char
**
err
);
/* Optional function to apply new value at runtime (generic doc above) */
int
(
*
is_valid_fn
)(
int
val
,
const
char
**
err
);
/* Optional function to check validity of new value (generic doc above) */
int
(
*
update_fn
)(
int
val
,
int
prev
,
const
char
**
err
);
/* Optional function to apply new value at runtime (generic doc above) */
}
boolConfigData
;
typedef
struct
stringConfigData
{
char
**
config
;
/* Pointer to the server config this value is stored in. */
const
char
*
default_value
;
/* Default value of the config on rewrite. */
int
(
*
is_valid_fn
)(
char
*
val
,
char
**
err
);
/* Optional function to check validity of new value (generic doc above) */
int
(
*
update_fn
)(
char
*
val
,
char
*
prev
,
char
**
err
);
/* Optional function to apply new value at runtime (generic doc above) */
int
(
*
is_valid_fn
)(
char
*
val
,
const
char
**
err
);
/* Optional function to check validity of new value (generic doc above) */
int
(
*
update_fn
)(
char
*
val
,
char
*
prev
,
const
char
**
err
);
/* Optional function to apply new value at runtime (generic doc above) */
int
convert_empty_to_null
;
/* Boolean indicating if empty strings should
be stored as a NULL value. */
}
stringConfigData
;
...
...
@@ -169,8 +169,8 @@ typedef struct stringConfigData {
typedef
struct
sdsConfigData
{
sds
*
config
;
/* Pointer to the server config this value is stored in. */
const
char
*
default_value
;
/* Default value of the config on rewrite. */
int
(
*
is_valid_fn
)(
sds
val
,
char
**
err
);
/* Optional function to check validity of new value (generic doc above) */
int
(
*
update_fn
)(
sds
val
,
sds
prev
,
char
**
err
);
/* Optional function to apply new value at runtime (generic doc above) */
int
(
*
is_valid_fn
)(
sds
val
,
const
char
**
err
);
/* Optional function to check validity of new value (generic doc above) */
int
(
*
update_fn
)(
sds
val
,
sds
prev
,
const
char
**
err
);
/* Optional function to apply new value at runtime (generic doc above) */
int
convert_empty_to_null
;
/* Boolean indicating if empty SDS strings should
be stored as a NULL value. */
}
sdsConfigData
;
...
...
@@ -179,8 +179,8 @@ typedef struct enumConfigData {
int
*
config
;
/* The pointer to the server config this value is stored in */
configEnum
*
enum_value
;
/* The underlying enum type this data represents */
const
int
default_value
;
/* The default value of the config on rewrite */
int
(
*
is_valid_fn
)(
int
val
,
char
**
err
);
/* Optional function to check validity of new value (generic doc above) */
int
(
*
update_fn
)(
int
val
,
int
prev
,
char
**
err
);
/* Optional function to apply new value at runtime (generic doc above) */
int
(
*
is_valid_fn
)(
int
val
,
const
char
**
err
);
/* Optional function to check validity of new value (generic doc above) */
int
(
*
update_fn
)(
int
val
,
int
prev
,
const
char
**
err
);
/* Optional function to apply new value at runtime (generic doc above) */
}
enumConfigData
;
typedef
enum
numericType
{
...
...
@@ -214,8 +214,8 @@ typedef struct numericConfigData {
long
long
lower_bound
;
/* The lower bound of this numeric value */
long
long
upper_bound
;
/* The upper bound of this numeric value */
const
long
long
default_value
;
/* The default value of the config on rewrite */
int
(
*
is_valid_fn
)(
long
long
val
,
char
**
err
);
/* Optional function to check validity of new value (generic doc above) */
int
(
*
update_fn
)(
long
long
val
,
long
long
prev
,
char
**
err
);
/* Optional function to apply new value at runtime (generic doc above) */
int
(
*
is_valid_fn
)(
long
long
val
,
const
char
**
err
);
/* Optional function to check validity of new value (generic doc above) */
int
(
*
update_fn
)(
long
long
val
,
long
long
prev
,
const
char
**
err
);
/* Optional function to apply new value at runtime (generic doc above) */
}
numericConfigData
;
typedef
union
typeData
{
...
...
@@ -230,10 +230,10 @@ typedef struct typeInterface {
/* Called on server start, to init the server with default value */
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
,
char
**
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
* and can set a verbose err string, update is true when called from CONFIG SET */
int
(
*
set
)(
typeData
data
,
sds
value
,
int
update
,
char
**
err
);
int
(
*
set
)(
typeData
data
,
sds
value
,
int
update
,
const
char
**
err
);
/* Called on CONFIG GET, required to add output to the client */
void
(
*
get
)(
client
*
c
,
typeData
data
);
/* Called on CONFIG REWRITE, required to rewrite the config state */
...
...
@@ -325,7 +325,7 @@ void queueLoadModule(sds path, sds *argv, int argc) {
* server.oom_score_adj_values if valid.
*/
static
int
updateOOMScoreAdjValues
(
sds
*
args
,
char
**
err
,
int
apply
)
{
static
int
updateOOMScoreAdjValues
(
sds
*
args
,
const
char
**
err
,
int
apply
)
{
int
i
;
int
values
[
CONFIG_OOM_COUNT
];
...
...
@@ -385,7 +385,7 @@ void initConfigValues() {
}
void
loadServerConfigFromString
(
char
*
config
)
{
char
*
err
=
NULL
;
const
char
*
err
=
NULL
;
int
linenum
=
0
,
totlines
,
i
;
int
slaveof_linenum
=
0
;
sds
*
lines
;
...
...
@@ -608,7 +608,7 @@ void loadServerConfigFromString(char *config) {
int
argc_err
;
if
(
ACLAppendUserForLoading
(
argv
,
argc
,
&
argc_err
)
==
C_ERR
)
{
char
buf
[
1024
];
char
*
errmsg
=
ACLSetUserStringError
();
const
char
*
errmsg
=
ACLSetUserStringError
();
snprintf
(
buf
,
sizeof
(
buf
),
"Error in user declaration '%s': %s"
,
argv
[
argc_err
],
errmsg
);
err
=
buf
;
...
...
@@ -624,8 +624,7 @@ void loadServerConfigFromString(char *config) {
err
=
"sentinel directive while not in sentinel mode"
;
goto
loaderr
;
}
err
=
sentinelHandleConfiguration
(
argv
+
1
,
argc
-
1
);
if
(
err
)
goto
loaderr
;
queueSentinelConfig
(
argv
+
1
,
argc
-
1
,
linenum
,
lines
[
i
]);
}
}
else
{
err
=
"Bad directive or wrong number of arguments"
;
goto
loaderr
;
...
...
@@ -730,7 +729,7 @@ void configSetCommand(client *c) {
robj
*
o
;
long
long
ll
;
int
err
;
char
*
errstr
=
NULL
;
const
char
*
errstr
=
NULL
;
serverAssertWithInfo
(
c
,
c
->
argv
[
2
],
sdsEncodedObject
(
c
->
argv
[
2
]));
serverAssertWithInfo
(
c
,
c
->
argv
[
3
],
sdsEncodedObject
(
c
->
argv
[
3
]));
o
=
c
->
argv
[
3
];
...
...
@@ -1221,7 +1220,16 @@ struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
sdsfree
(
argv
[
0
]);
argv
[
0
]
=
alt
;
}
rewriteConfigAddLineNumberToOption
(
state
,
argv
[
0
],
linenum
);
/* If this is sentinel config, we use sentinel "sentinel <config>" as option
to avoid messing up the sequence. */
if
(
server
.
sentinel_mode
&&
argc
>
1
&&
!
strcasecmp
(
argv
[
0
],
"sentinel"
))
{
sds
sentinelOption
=
sdsempty
();
sentinelOption
=
sdscatfmt
(
sentinelOption
,
"%S %S"
,
argv
[
0
],
argv
[
1
]);
rewriteConfigAddLineNumberToOption
(
state
,
sentinelOption
,
linenum
);
sdsfree
(
sentinelOption
);
}
else
{
rewriteConfigAddLineNumberToOption
(
state
,
argv
[
0
],
linenum
);
}
sdsfreesplitres
(
argv
,
argc
);
}
fclose
(
fp
);
...
...
@@ -1683,7 +1691,7 @@ int rewriteConfigOverwriteFile(char *configfile, sds content) {
if
(
fsync
(
fd
))
serverLog
(
LL_WARNING
,
"Could not sync tmp config file to disk (%s)"
,
strerror
(
errno
));
else
if
(
fchmod
(
fd
,
0644
)
==
-
1
)
else
if
(
fchmod
(
fd
,
0644
&
~
server
.
umask
)
==
-
1
)
serverLog
(
LL_WARNING
,
"Could not chmod config file (%s)"
,
strerror
(
errno
));
else
if
(
rename
(
tmp_conffile
,
configfile
)
==
-
1
)
serverLog
(
LL_WARNING
,
"Could not rename tmp config file (%s)"
,
strerror
(
errno
));
...
...
@@ -1795,7 +1803,7 @@ static void boolConfigInit(typeData data) {
*
data
.
yesno
.
config
=
data
.
yesno
.
default_value
;
}
static
int
boolConfigSet
(
typeData
data
,
sds
value
,
int
update
,
char
**
err
)
{
static
int
boolConfigSet
(
typeData
data
,
sds
value
,
int
update
,
const
char
**
err
)
{
int
yn
=
yesnotoi
(
value
);
if
(
yn
==
-
1
)
{
*
err
=
"argument must be 'yes' or 'no'"
;
...
...
@@ -1836,7 +1844,7 @@ static void stringConfigInit(typeData data) {
*
data
.
string
.
config
=
(
data
.
string
.
convert_empty_to_null
&&
!
data
.
string
.
default_value
)
?
NULL
:
zstrdup
(
data
.
string
.
default_value
);
}
static
int
stringConfigSet
(
typeData
data
,
sds
value
,
int
update
,
char
**
err
)
{
static
int
stringConfigSet
(
typeData
data
,
sds
value
,
int
update
,
const
char
**
err
)
{
if
(
data
.
string
.
is_valid_fn
&&
!
data
.
string
.
is_valid_fn
(
value
,
err
))
return
0
;
char
*
prev
=
*
data
.
string
.
config
;
...
...
@@ -1863,7 +1871,7 @@ static void sdsConfigInit(typeData data) {
*
data
.
sds
.
config
=
(
data
.
sds
.
convert_empty_to_null
&&
!
data
.
sds
.
default_value
)
?
NULL
:
sdsnew
(
data
.
sds
.
default_value
);
}
static
int
sdsConfigSet
(
typeData
data
,
sds
value
,
int
update
,
char
**
err
)
{
static
int
sdsConfigSet
(
typeData
data
,
sds
value
,
int
update
,
const
char
**
err
)
{
if
(
data
.
sds
.
is_valid_fn
&&
!
data
.
sds
.
is_valid_fn
(
value
,
err
))
return
0
;
sds
prev
=
*
data
.
sds
.
config
;
...
...
@@ -1922,7 +1930,7 @@ static void enumConfigInit(typeData data) {
*
data
.
enumd
.
config
=
data
.
enumd
.
default_value
;
}
static
int
enumConfigSet
(
typeData
data
,
sds
value
,
int
update
,
char
**
err
)
{
static
int
enumConfigSet
(
typeData
data
,
sds
value
,
int
update
,
const
char
**
err
)
{
int
enumval
=
configEnumGetValue
(
data
.
enumd
.
enum_value
,
value
);
if
(
enumval
==
INT_MIN
)
{
sds
enumerr
=
sdsnew
(
"argument must be one of the following: "
);
...
...
@@ -2028,7 +2036,7 @@ static void numericConfigInit(typeData data) {
SET_NUMERIC_TYPE
(
data
.
numeric
.
default_value
)
}
static
int
numericBoundaryCheck
(
typeData
data
,
long
long
ll
,
char
**
err
)
{
static
int
numericBoundaryCheck
(
typeData
data
,
long
long
ll
,
const
char
**
err
)
{
if
(
data
.
numeric
.
numeric_type
==
NUMERIC_TYPE_ULONG_LONG
||
data
.
numeric
.
numeric_type
==
NUMERIC_TYPE_UINT
||
data
.
numeric
.
numeric_type
==
NUMERIC_TYPE_SIZE_T
)
{
...
...
@@ -2058,7 +2066,7 @@ static int numericBoundaryCheck(typeData data, long long ll, char **err) {
return
1
;
}
static
int
numericConfigSet
(
typeData
data
,
sds
value
,
int
update
,
char
**
err
)
{
static
int
numericConfigSet
(
typeData
data
,
sds
value
,
int
update
,
const
char
**
err
)
{
long
long
ll
,
prev
=
0
;
if
(
data
.
numeric
.
is_memory
)
{
int
memerr
;
...
...
@@ -2196,7 +2204,7 @@ static void numericConfigRewrite(typeData data, const char *name, struct rewrite
} \
}
static
int
isValidActiveDefrag
(
int
val
,
char
**
err
)
{
static
int
isValidActiveDefrag
(
int
val
,
const
char
**
err
)
{
#ifndef HAVE_DEFRAG
if
(
val
)
{
*
err
=
"Active defragmentation cannot be enabled: it "
...
...
@@ -2212,7 +2220,7 @@ static int isValidActiveDefrag(int val, char **err) {
return
1
;
}
static
int
isValidDBfilename
(
char
*
val
,
char
**
err
)
{
static
int
isValidDBfilename
(
char
*
val
,
const
char
**
err
)
{
if
(
!
pathIsBaseName
(
val
))
{
*
err
=
"dbfilename can't be a path, just a filename"
;
return
0
;
...
...
@@ -2220,7 +2228,7 @@ static int isValidDBfilename(char *val, char **err) {
return
1
;
}
static
int
isValidAOFfilename
(
char
*
val
,
char
**
err
)
{
static
int
isValidAOFfilename
(
char
*
val
,
const
char
**
err
)
{
if
(
!
pathIsBaseName
(
val
))
{
*
err
=
"appendfilename can't be a path, just a filename"
;
return
0
;
...
...
@@ -2228,7 +2236,26 @@ static int isValidAOFfilename(char *val, char **err) {
return
1
;
}
static
int
updateHZ
(
long
long
val
,
long
long
prev
,
char
**
err
)
{
/* Validate specified string is a valid proc-title-template */
static
int
isValidProcTitleTemplate
(
char
*
val
,
const
char
**
err
)
{
if
(
!
validateProcTitleTemplate
(
val
))
{
*
err
=
"template format is invalid or contains unknown variables"
;
return
0
;
}
return
1
;
}
static
int
updateProcTitleTemplate
(
char
*
val
,
char
*
prev
,
const
char
**
err
)
{
UNUSED
(
val
);
UNUSED
(
prev
);
if
(
redisSetProcTitle
(
NULL
)
==
C_ERR
)
{
*
err
=
"failed to set process title"
;
return
0
;
}
return
1
;
}
static
int
updateHZ
(
long
long
val
,
long
long
prev
,
const
char
**
err
)
{
UNUSED
(
prev
);
UNUSED
(
err
);
/* Hz is more a hint from the user, so we accept values out of range
...
...
@@ -2240,14 +2267,14 @@ static int updateHZ(long long val, long long prev, char **err) {
return
1
;
}
static
int
updateJemallocBgThread
(
int
val
,
int
prev
,
char
**
err
)
{
static
int
updateJemallocBgThread
(
int
val
,
int
prev
,
const
char
**
err
)
{
UNUSED
(
prev
);
UNUSED
(
err
);
set_jemalloc_bg_thread
(
val
);
return
1
;
}
static
int
updateReplBacklogSize
(
long
long
val
,
long
long
prev
,
char
**
err
)
{
static
int
updateReplBacklogSize
(
long
long
val
,
long
long
prev
,
const
char
**
err
)
{
/* resizeReplicationBacklog sets server.repl_backlog_size, and relies on
* being able to tell when the size changes, so restore prev before calling it. */
UNUSED
(
err
);
...
...
@@ -2256,7 +2283,7 @@ static int updateReplBacklogSize(long long val, long long prev, char **err) {
return
1
;
}
static
int
updateMaxmemory
(
long
long
val
,
long
long
prev
,
char
**
err
)
{
static
int
updateMaxmemory
(
long
long
val
,
long
long
prev
,
const
char
**
err
)
{
UNUSED
(
prev
);
UNUSED
(
err
);
if
(
val
)
{
...
...
@@ -2269,7 +2296,7 @@ static int updateMaxmemory(long long val, long long prev, char **err) {
return
1
;
}
static
int
updateGoodSlaves
(
long
long
val
,
long
long
prev
,
char
**
err
)
{
static
int
updateGoodSlaves
(
long
long
val
,
long
long
prev
,
const
char
**
err
)
{
UNUSED
(
val
);
UNUSED
(
prev
);
UNUSED
(
err
);
...
...
@@ -2277,7 +2304,7 @@ static int updateGoodSlaves(long long val, long long prev, char **err) {
return
1
;
}
static
int
updateAppendonly
(
int
val
,
int
prev
,
char
**
err
)
{
static
int
updateAppendonly
(
int
val
,
int
prev
,
const
char
**
err
)
{
UNUSED
(
prev
);
if
(
val
==
0
&&
server
.
aof_state
!=
AOF_OFF
)
{
stopAppendOnly
();
...
...
@@ -2290,7 +2317,7 @@ static int updateAppendonly(int val, int prev, char **err) {
return
1
;
}
static
int
updateSighandlerEnabled
(
int
val
,
int
prev
,
char
**
err
)
{
static
int
updateSighandlerEnabled
(
int
val
,
int
prev
,
const
char
**
err
)
{
UNUSED
(
err
);
UNUSED
(
prev
);
if
(
val
)
...
...
@@ -2300,7 +2327,7 @@ static int updateSighandlerEnabled(int val, int prev, char **err) {
return
1
;
}
static
int
updateMaxclients
(
long
long
val
,
long
long
prev
,
char
**
err
)
{
static
int
updateMaxclients
(
long
long
val
,
long
long
prev
,
const
char
**
err
)
{
/* Try to check if the OS is capable of supporting so many FDs. */
if
(
val
>
prev
)
{
adjustOpenFilesLimit
();
...
...
@@ -2328,7 +2355,7 @@ static int updateMaxclients(long long val, long long prev, char **err) {
return
1
;
}
static
int
updateOOMScoreAdj
(
int
val
,
int
prev
,
char
**
err
)
{
static
int
updateOOMScoreAdj
(
int
val
,
int
prev
,
const
char
**
err
)
{
UNUSED
(
prev
);
if
(
val
)
{
...
...
@@ -2342,7 +2369,7 @@ static int updateOOMScoreAdj(int val, int prev, char **err) {
}
#ifdef USE_OPENSSL
static
int
updateTlsCfg
(
char
*
val
,
char
*
prev
,
char
**
err
)
{
static
int
updateTlsCfg
(
char
*
val
,
char
*
prev
,
const
char
**
err
)
{
UNUSED
(
val
);
UNUSED
(
prev
);
UNUSED
(
err
);
...
...
@@ -2355,13 +2382,13 @@ static int updateTlsCfg(char *val, char *prev, char **err) {
}
return
1
;
}
static
int
updateTlsCfgBool
(
int
val
,
int
prev
,
char
**
err
)
{
static
int
updateTlsCfgBool
(
int
val
,
int
prev
,
const
char
**
err
)
{
UNUSED
(
val
);
UNUSED
(
prev
);
return
updateTlsCfg
(
NULL
,
NULL
,
err
);
}
static
int
updateTlsCfgInt
(
long
long
val
,
long
long
prev
,
char
**
err
)
{
static
int
updateTlsCfgInt
(
long
long
val
,
long
long
prev
,
const
char
**
err
)
{
UNUSED
(
val
);
UNUSED
(
prev
);
return
updateTlsCfg
(
NULL
,
NULL
,
err
);
...
...
@@ -2380,11 +2407,13 @@ standardConfig configs[] = {
createBoolConfig
(
"rdb-del-sync-files"
,
NULL
,
MODIFIABLE_CONFIG
,
server
.
rdb_del_sync_files
,
0
,
NULL
,
NULL
),
createBoolConfig
(
"activerehashing"
,
NULL
,
MODIFIABLE_CONFIG
,
server
.
activerehashing
,
1
,
NULL
,
NULL
),
createBoolConfig
(
"stop-writes-on-bgsave-error"
,
NULL
,
MODIFIABLE_CONFIG
,
server
.
stop_writes_on_bgsave_err
,
1
,
NULL
,
NULL
),
createBoolConfig
(
"set-proc-title"
,
NULL
,
IMMUTABLE_CONFIG
,
server
.
set_proc_title
,
1
,
NULL
,
NULL
),
/* Should setproctitle be used? */
createBoolConfig
(
"dynamic-hz"
,
NULL
,
MODIFIABLE_CONFIG
,
server
.
dynamic_hz
,
1
,
NULL
,
NULL
),
/* Adapt hz to # of clients.*/
createBoolConfig
(
"lazyfree-lazy-eviction"
,
NULL
,
MODIFIABLE_CONFIG
,
server
.
lazyfree_lazy_eviction
,
0
,
NULL
,
NULL
),
createBoolConfig
(
"lazyfree-lazy-expire"
,
NULL
,
MODIFIABLE_CONFIG
,
server
.
lazyfree_lazy_expire
,
0
,
NULL
,
NULL
),
createBoolConfig
(
"lazyfree-lazy-server-del"
,
NULL
,
MODIFIABLE_CONFIG
,
server
.
lazyfree_lazy_server_del
,
0
,
NULL
,
NULL
),
createBoolConfig
(
"lazyfree-lazy-user-del"
,
NULL
,
MODIFIABLE_CONFIG
,
server
.
lazyfree_lazy_user_del
,
0
,
NULL
,
NULL
),
createBoolConfig
(
"lazyfree-lazy-user-flush"
,
NULL
,
MODIFIABLE_CONFIG
,
server
.
lazyfree_lazy_user_flush
,
0
,
NULL
,
NULL
),
createBoolConfig
(
"repl-disable-tcp-nodelay"
,
NULL
,
MODIFIABLE_CONFIG
,
server
.
repl_disable_tcp_nodelay
,
0
,
NULL
,
NULL
),
createBoolConfig
(
"repl-diskless-sync"
,
NULL
,
MODIFIABLE_CONFIG
,
server
.
repl_diskless_sync
,
0
,
NULL
,
NULL
),
createBoolConfig
(
"gopher-enabled"
,
NULL
,
MODIFIABLE_CONFIG
,
server
.
gopher_enabled
,
0
,
NULL
,
NULL
),
...
...
@@ -2425,6 +2454,7 @@ standardConfig configs[] = {
createStringConfig
(
"aof_rewrite_cpulist"
,
NULL
,
IMMUTABLE_CONFIG
,
EMPTY_STRING_IS_NULL
,
server
.
aof_rewrite_cpulist
,
NULL
,
NULL
,
NULL
),
createStringConfig
(
"bgsave_cpulist"
,
NULL
,
IMMUTABLE_CONFIG
,
EMPTY_STRING_IS_NULL
,
server
.
bgsave_cpulist
,
NULL
,
NULL
,
NULL
),
createStringConfig
(
"ignore-warnings"
,
NULL
,
MODIFIABLE_CONFIG
,
ALLOW_EMPTY_STRING
,
server
.
ignore_warnings
,
""
,
NULL
,
NULL
),
createStringConfig
(
"proc-title-template"
,
NULL
,
MODIFIABLE_CONFIG
,
ALLOW_EMPTY_STRING
,
server
.
proc_title_template
,
CONFIG_DEFAULT_PROC_TITLE_TEMPLATE
,
isValidProcTitleTemplate
,
updateProcTitleTemplate
),
/* SDS Configs */
createSDSConfig
(
"masterauth"
,
NULL
,
MODIFIABLE_CONFIG
,
EMPTY_STRING_IS_NULL
,
server
.
masterauth
,
NULL
,
NULL
,
NULL
),
...
...
src/db.c
View file @
2dba1e39
...
...
@@ -226,7 +226,7 @@ void dbOverwrite(redisDb *db, robj *key, robj *val) {
/* Although the key is not really deleted from the database, we regard
overwrite as two steps of unlink+add, so we still need to call the unlink
callback of the module. */
moduleNotifyKeyUnlink
(
key
,
val
);
moduleNotifyKeyUnlink
(
key
,
old
);
dictSetVal
(
db
->
dict
,
de
,
val
);
if
(
server
.
lazyfree_lazy_server_del
)
{
...
...
@@ -595,21 +595,23 @@ void signalFlushedDb(int dbid, int async) {
/* Return the set of flags to use for the emptyDb() call for FLUSHALL
* and FLUSHDB commands.
*
* Currently the command just attempts to parse the "ASYNC" option. It
* also checks if the command arity is wrong.
* sync: flushes the database in an sync manner.
* async: flushes the database in an async manner.
* no option: determine sync or async according to the value of lazyfree-lazy-user-flush.
*
* On success C_OK is returned and the flags are stored in *flags, otherwise
* C_ERR is returned and the function sends an error to the client. */
int
getFlushCommandFlags
(
client
*
c
,
int
*
flags
)
{
/* Parse the optional ASYNC option. */
if
(
c
->
argc
>
1
)
{
if
(
c
->
argc
>
2
||
strcasecmp
(
c
->
argv
[
1
]
->
ptr
,
"async"
))
{
addReplyErrorObject
(
c
,
shared
.
syntaxerr
);
return
C_ERR
;
}
if
(
c
->
argc
==
2
&&
!
strcasecmp
(
c
->
argv
[
1
]
->
ptr
,
"sync"
))
{
*
flags
=
EMPTYDB_NO_FLAGS
;
}
else
if
(
c
->
argc
==
2
&&
!
strcasecmp
(
c
->
argv
[
1
]
->
ptr
,
"async"
))
{
*
flags
=
EMPTYDB_ASYNC
;
}
else
if
(
c
->
argc
==
1
)
{
*
flags
=
server
.
lazyfree_lazy_user_flush
?
EMPTYDB_ASYNC
:
EMPTYDB_NO_FLAGS
;
}
else
{
*
flags
=
EMPTYDB_NO_FLAGS
;
addReplyErrorObject
(
c
,
shared
.
syntaxerr
);
return
C_ERR
;
}
return
C_OK
;
}
...
...
@@ -951,7 +953,7 @@ void scanGenericCommand(client *c, robj *o, unsigned long cursor) {
int
filter
=
0
;
/* Filter element if it does not match the pattern. */
if
(
!
filter
&&
use_pattern
)
{
if
(
use_pattern
)
{
if
(
sdsEncodedObject
(
kobj
))
{
if
(
!
stringmatchlen
(
pat
,
patlen
,
kobj
->
ptr
,
sdslen
(
kobj
->
ptr
),
0
))
filter
=
1
;
...
...
src/defrag.c
View file @
2dba1e39
...
...
@@ -367,7 +367,7 @@ long activeDefragSdsListAndDict(list *l, dict *d, int dict_val_type) {
}
else
if
(
dict_val_type
==
DEFRAG_SDS_DICT_VAL_VOID_PTR
)
{
void
*
newptr
,
*
ptr
=
dictGetVal
(
de
);
if
((
newptr
=
activeDefragAlloc
(
ptr
)))
ln
->
val
ue
=
newptr
,
defragged
++
;
de
->
v
.
v
al
=
newptr
,
defragged
++
;
}
defragged
+=
dictIterDefragEntry
(
di
);
}
...
...
Prev
1
2
3
4
5
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment