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
70469b76
Unverified
Commit
70469b76
authored
Nov 21, 2019
by
Salvatore Sanfilippo
Committed by
GitHub
Nov 21, 2019
Browse files
Merge branch 'unstable' into expose_zmalloc_capabilities
parents
e45239e3
c697edf4
Changes
31
Expand all
Hide whitespace changes
Inline
Side-by-side
redis.conf
View file @
70469b76
...
...
@@ -813,11 +813,11 @@ replica-priority 100
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
#
# volatile-lru -> Evict using approximated LRU
among the
keys with an expire set.
# volatile-lru -> Evict using approximated LRU
, only
keys with an expire set.
# allkeys-lru -> Evict any key using approximated LRU.
# volatile-lfu -> Evict using approximated LFU
among the
keys with an expire set.
# volatile-lfu -> Evict using approximated LFU
, only
keys with an expire set.
# allkeys-lfu -> Evict any key using approximated LFU.
# volatile-random -> Remove a random key
among the ones with
an expire set.
# volatile-random -> Remove a random key
having
an expire set.
# allkeys-random -> Remove a random key, any key.
# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
# noeviction -> Don't evict anything, just return an error on write operations.
...
...
@@ -872,6 +872,23 @@ replica-priority 100
#
# replica-ignore-maxmemory yes
# Redis reclaims expired keys in two ways: upon access when those keys are
# found to be expired, and also in background, in what is called the
# "active expire key". The key space is slowly and interactively scanned
# looking for expired keys to reclaim, so that it is possible to free memory
# of keys that are expired and will never be accessed again in a short time.
#
# The default effort of the expire cycle will try to avoid having more than
# ten percent of expired keys still in memory, and will try to avoid consuming
# more than 25% of total memory and to add latency to the system. However
# it is possible to increase the expire "effort" that is normally set to
# "1", to a greater value, up to the value "10". At its maximum value the
# system will use more CPU, longer cycles (and technically may introduce
# more latency), and will tollerate less already expired keys still present
# in the system. It's a tradeoff betweeen memory, CPU and latecy.
#
# active-expire-effort 1
############################# LAZY FREEING ####################################
# Redis has two primitives to delete keys. One is called DEL and is a blocking
...
...
@@ -1606,10 +1623,6 @@ rdb-save-incremental-fsync yes
########################### ACTIVE DEFRAGMENTATION #######################
#
# WARNING THIS FEATURE IS EXPERIMENTAL. However it was stress tested
# even in production and manually tested by multiple engineers for some
# time.
#
# What is active defragmentation?
# -------------------------------
#
...
...
@@ -1649,7 +1662,7 @@ rdb-save-incremental-fsync yes
# a good idea to leave the defaults untouched.
# Enabled active defragmentation
# activedefrag
yes
# activedefrag
no
# Minimum amount of fragmentation waste to start active defrag
# active-defrag-ignore-bytes 100mb
...
...
@@ -1660,11 +1673,13 @@ rdb-save-incremental-fsync yes
# Maximum percentage of fragmentation at which we use maximum effort
# active-defrag-threshold-upper 100
# Minimal effort for defrag in CPU percentage
# active-defrag-cycle-min 5
# Minimal effort for defrag in CPU percentage, to be used when the lower
# threshold is reached
# active-defrag-cycle-min 1
# Maximal effort for defrag in CPU percentage
# active-defrag-cycle-max 75
# Maximal effort for defrag in CPU percentage, to be used when the upper
# threshold is reached
# active-defrag-cycle-max 25
# Maximum number of set/hash/zset/list fields that will be processed from
# the main dictionary scan
...
...
runtest-moduleapi
View file @
70469b76
...
...
@@ -22,4 +22,6 @@ $TCLSH tests/test_helper.tcl \
--single
unit/moduleapi/hooks
\
--single
unit/moduleapi/misc
\
--single
unit/moduleapi/blockonkeys
\
--single
unit/moduleapi/scan
\
--single
unit/moduleapi/datatype
\
"
${
@
}
"
src/adlist.h
View file @
70469b76
...
...
@@ -66,7 +66,7 @@ typedef struct list {
#define listSetMatchMethod(l,m) ((l)->match = (m))
#define listGetDupMethod(l) ((l)->dup)
#define listGetFree(l) ((l)->free)
#define listGetFree
Method
(l) ((l)->free)
#define listGetMatchMethod(l) ((l)->match)
/* Prototypes */
...
...
src/aof.c
View file @
70469b76
...
...
@@ -1766,7 +1766,7 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
server
.
aof_selected_db
=
-
1
;
/* Make sure SELECT is re-issued */
aofUpdateCurrentSize
();
server
.
aof_rewrite_base_size
=
server
.
aof_current_size
;
server
.
aof_
current_size
=
server
.
aof_current_size
;
server
.
aof_
fsync_offset
=
server
.
aof_current_size
;
/* Clear regular AOF buffer since its contents was just written to
* the new AOF from the background rewrite buffer. */
...
...
src/blocked.c
View file @
70469b76
...
...
@@ -514,6 +514,16 @@ void handleClientsBlockedOnKeys(void) {
* we can safely call signalKeyAsReady() against this key. */
dictDelete
(
rl
->
db
->
ready_keys
,
rl
->
key
);
/* Even if we are not inside call(), increment the call depth
* in order to make sure that keys are expired against a fixed
* reference time, and not against the wallclock time. This
* way we can lookup an object multiple times (BRPOPLPUSH does
* that) without the risk of it being freed in the second
* lookup, invalidating the first one.
* See https://github.com/antirez/redis/pull/6554. */
server
.
fixed_time_expire
++
;
updateCachedTime
(
0
);
/* Serve clients blocked on list key. */
robj
*
o
=
lookupKeyWrite
(
rl
->
db
,
rl
->
key
);
...
...
@@ -529,6 +539,7 @@ void handleClientsBlockedOnKeys(void) {
* module is trying to accomplish right now. */
serveClientsBlockedOnKeyByModule
(
rl
);
}
server
.
fixed_time_expire
--
;
/* Free this item. */
decrRefCount
(
rl
->
key
);
...
...
src/cluster.c
View file @
70469b76
...
...
@@ -4966,7 +4966,7 @@ void restoreCommand(client *c) {
if
(
!
absttl
)
ttl
+=
mstime
();
setExpire
(
c
,
c
->
db
,
c
->
argv
[
1
],
ttl
);
}
objectSetLRUOrLFU
(
obj
,
lfu_freq
,
lru_idle
,
lru_clock
);
objectSetLRUOrLFU
(
obj
,
lfu_freq
,
lru_idle
,
lru_clock
,
1000
);
signalModifiedKey
(
c
->
db
,
c
->
argv
[
1
]);
addReply
(
c
,
shared
.
ok
);
server
.
dirty
++
;
...
...
src/config.c
View file @
70469b76
...
...
@@ -256,7 +256,7 @@ void loadServerConfigFromString(char *config) {
for
(
configYesNo
*
config
=
configs_yesno
;
config
->
name
!=
NULL
;
config
++
)
{
if
((
!
strcasecmp
(
argv
[
0
],
config
->
name
)
||
(
config
->
alias
&&
!
strcasecmp
(
argv
[
0
],
config
->
alias
)))
&&
(
argc
==
2
))
(
argc
==
2
))
{
if
((
*
(
config
->
config
)
=
yesnotoi
(
argv
[
1
]))
==
-
1
)
{
err
=
"argument must be 'yes' or 'no'"
;
goto
loaderr
;
...
...
@@ -580,6 +580,14 @@ void loadServerConfigFromString(char *config) {
err
=
"active-defrag-max-scan-fields must be positive"
;
goto
loaderr
;
}
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"active-expire-effort"
)
&&
argc
==
2
)
{
server
.
active_expire_effort
=
atoi
(
argv
[
1
]);
if
(
server
.
active_expire_effort
<
1
||
server
.
active_expire_effort
>
10
)
{
err
=
"active-expire-effort must be between 1 and 10"
;
goto
loaderr
;
}
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"hash-max-ziplist-entries"
)
&&
argc
==
2
)
{
server
.
hash_max_ziplist_entries
=
memtoll
(
argv
[
1
],
NULL
);
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"hash-max-ziplist-value"
)
&&
argc
==
2
)
{
...
...
@@ -1165,6 +1173,8 @@ void configSetCommand(client *c) {
"active-defrag-cycle-max"
,
server
.
active_defrag_cycle_max
,
1
,
99
)
{
}
config_set_numerical_field
(
"active-defrag-max-scan-fields"
,
server
.
active_defrag_max_scan_fields
,
1
,
LONG_MAX
)
{
}
config_set_numerical_field
(
"active-expire-effort"
,
server
.
active_expire_effort
,
1
,
10
)
{
}
config_set_numerical_field
(
"auto-aof-rewrite-percentage"
,
server
.
aof_rewrite_perc
,
0
,
INT_MAX
){
}
config_set_numerical_field
(
...
...
@@ -1478,6 +1488,7 @@ void configGetCommand(client *c) {
config_get_numerical_field
(
"active-defrag-cycle-min"
,
server
.
active_defrag_cycle_min
);
config_get_numerical_field
(
"active-defrag-cycle-max"
,
server
.
active_defrag_cycle_max
);
config_get_numerical_field
(
"active-defrag-max-scan-fields"
,
server
.
active_defrag_max_scan_fields
);
config_get_numerical_field
(
"active-expire-effort"
,
server
.
active_expire_effort
);
config_get_numerical_field
(
"auto-aof-rewrite-percentage"
,
server
.
aof_rewrite_perc
);
config_get_numerical_field
(
"auto-aof-rewrite-min-size"
,
...
...
@@ -2327,6 +2338,7 @@ int rewriteConfig(char *path) {
rewriteConfigNumericalOption
(
state
,
"active-defrag-cycle-min"
,
server
.
active_defrag_cycle_min
,
CONFIG_DEFAULT_DEFRAG_CYCLE_MIN
);
rewriteConfigNumericalOption
(
state
,
"active-defrag-cycle-max"
,
server
.
active_defrag_cycle_max
,
CONFIG_DEFAULT_DEFRAG_CYCLE_MAX
);
rewriteConfigNumericalOption
(
state
,
"active-defrag-max-scan-fields"
,
server
.
active_defrag_max_scan_fields
,
CONFIG_DEFAULT_DEFRAG_MAX_SCAN_FIELDS
);
rewriteConfigNumericalOption
(
state
,
"active-expire-effort"
,
server
.
active_expire_effort
,
CONFIG_DEFAULT_ACTIVE_EXPIRE_EFFORT
);
rewriteConfigYesNoOption
(
state
,
"appendonly"
,
server
.
aof_enabled
,
0
);
rewriteConfigStringOption
(
state
,
"appendfilename"
,
server
.
aof_filename
,
CONFIG_DEFAULT_AOF_FILENAME
);
rewriteConfigEnumOption
(
state
,
"appendfsync"
,
server
.
aof_fsync
,
aof_fsync_enum
,
CONFIG_DEFAULT_AOF_FSYNC
);
...
...
src/db.c
View file @
70469b76
...
...
@@ -1077,10 +1077,12 @@ int dbSwapDatabases(long id1, long id2) {
db1
->
dict
=
db2
->
dict
;
db1
->
expires
=
db2
->
expires
;
db1
->
avg_ttl
=
db2
->
avg_ttl
;
db1
->
expires_cursor
=
db2
->
expires_cursor
;
db2
->
dict
=
aux
.
dict
;
db2
->
expires
=
aux
.
expires
;
db2
->
avg_ttl
=
aux
.
avg_ttl
;
db2
->
expires_cursor
=
aux
.
expires_cursor
;
/* Now we need to handle clients blocked on lists: as an effect
* of swapping the two DBs, a client that was waiting for list
...
...
@@ -1196,6 +1198,7 @@ void propagateExpire(redisDb *db, robj *key, int lazy) {
/* Check if the key is expired. */
int
keyIsExpired
(
redisDb
*
db
,
robj
*
key
)
{
mstime_t
when
=
getExpire
(
db
,
key
);
mstime_t
now
;
if
(
when
<
0
)
return
0
;
/* No expire for this key */
...
...
@@ -1207,8 +1210,26 @@ int keyIsExpired(redisDb *db, robj *key) {
* only the first time it is accessed and not in the middle of the
* script execution, making propagation to slaves / AOF consistent.
* See issue #1525 on Github for more information. */
mstime_t
now
=
server
.
lua_caller
?
server
.
lua_time_start
:
mstime
();
if
(
server
.
lua_caller
)
{
now
=
server
.
lua_time_start
;
}
/* If we are in the middle of a command execution, we still want to use
* a reference time that does not change: in that case we just use the
* cached time, that we update before each call in the call() function.
* This way we avoid that commands such as RPOPLPUSH or similar, that
* may re-open the same key multiple times, can invalidate an already
* open object in a next call, if the next call will see the key expired,
* while the first did not. */
else
if
(
server
.
fixed_time_expire
>
0
)
{
now
=
server
.
mstime
;
}
/* For the other cases, we want to use the most fresh time we have. */
else
{
now
=
mstime
();
}
/* The key expired if the current (virtual or real) time is greater
* than the expire time of the key. */
return
now
>
when
;
}
...
...
src/defrag.c
View file @
70469b76
...
...
@@ -919,10 +919,12 @@ int defragLaterItem(dictEntry *de, unsigned long *cursor, long long endtime) {
return
0
;
}
/* static variables serving defragLaterStep to continue scanning a key from were we stopped last time. */
static
sds
defrag_later_current_key
=
NULL
;
static
unsigned
long
defrag_later_cursor
=
0
;
/* returns 0 if no more work needs to be been done, and 1 if time is up and more work is needed. */
int
defragLaterStep
(
redisDb
*
db
,
long
long
endtime
)
{
static
sds
current_key
=
NULL
;
static
unsigned
long
cursor
=
0
;
unsigned
int
iterations
=
0
;
unsigned
long
long
prev_defragged
=
server
.
stat_active_defrag_hits
;
unsigned
long
long
prev_scanned
=
server
.
stat_active_defrag_scanned
;
...
...
@@ -930,16 +932,15 @@ int defragLaterStep(redisDb *db, long long endtime) {
do
{
/* if we're not continuing a scan from the last call or loop, start a new one */
if
(
!
cursor
)
{
if
(
!
defrag_later_
cursor
)
{
listNode
*
head
=
listFirst
(
db
->
defrag_later
);
/* Move on to next key */
if
(
current_key
)
{
serverAssert
(
current_key
==
head
->
value
);
sdsfree
(
head
->
value
);
if
(
defrag_later_current_key
)
{
serverAssert
(
defrag_later_current_key
==
head
->
value
);
listDelNode
(
db
->
defrag_later
,
head
);
cursor
=
0
;
current_key
=
NULL
;
defrag_later_
cursor
=
0
;
defrag_later_
current_key
=
NULL
;
}
/* stop if we reached the last one. */
...
...
@@ -948,21 +949,21 @@ int defragLaterStep(redisDb *db, long long endtime) {
return
0
;
/* start a new key */
current_key
=
head
->
value
;
cursor
=
0
;
defrag_later_
current_key
=
head
->
value
;
defrag_later_
cursor
=
0
;
}
/* each time we enter this function we need to fetch the key from the dict again (if it still exists) */
dictEntry
*
de
=
dictFind
(
db
->
dict
,
current_key
);
dictEntry
*
de
=
dictFind
(
db
->
dict
,
defrag_later_
current_key
);
key_defragged
=
server
.
stat_active_defrag_hits
;
do
{
int
quit
=
0
;
if
(
defragLaterItem
(
de
,
&
cursor
,
endtime
))
if
(
defragLaterItem
(
de
,
&
defrag_later_
cursor
,
endtime
))
quit
=
1
;
/* time is up, we didn't finish all the work */
/* Don't start a new BIG key in this loop, this is because the
* next key can be a list, and scanLaterList must be done in once cycle */
if
(
!
cursor
)
if
(
!
defrag_later_
cursor
)
quit
=
1
;
/* Once in 16 scan iterations, 512 pointer reallocations, or 64 fields
...
...
@@ -982,7 +983,7 @@ int defragLaterStep(redisDb *db, long long endtime) {
prev_defragged
=
server
.
stat_active_defrag_hits
;
prev_scanned
=
server
.
stat_active_defrag_scanned
;
}
}
while
(
cursor
);
}
while
(
defrag_later_
cursor
);
if
(
key_defragged
!=
server
.
stat_active_defrag_hits
)
server
.
stat_active_defrag_key_hits
++
;
else
...
...
@@ -1039,6 +1040,21 @@ void activeDefragCycle(void) {
mstime_t
latency
;
int
quit
=
0
;
if
(
!
server
.
active_defrag_enabled
)
{
if
(
server
.
active_defrag_running
)
{
/* if active defrag was disabled mid-run, start from fresh next time. */
server
.
active_defrag_running
=
0
;
if
(
db
)
listEmpty
(
db
->
defrag_later
);
defrag_later_current_key
=
NULL
;
defrag_later_cursor
=
0
;
current_db
=
-
1
;
cursor
=
0
;
db
=
NULL
;
}
return
;
}
if
(
hasActiveChildProcess
())
return
;
/* Defragging memory while there's a fork will just do damage. */
...
...
src/expire.c
View file @
70469b76
...
...
@@ -78,24 +78,63 @@ int activeExpireCycleTryExpire(redisDb *db, dictEntry *de, long long now) {
* it will get more aggressive to avoid that too much memory is used by
* keys that can be removed from the keyspace.
*
* No more than CRON_DBS_PER_CALL databases are tested at every
* iteration.
* Every expire cycle tests multiple databases: the next call will start
* again from the next db, with the exception of exists for time limit: in that
* case we restart again from the last database we were processing. Anyway
* no more than CRON_DBS_PER_CALL databases are tested at every iteration.
*
* This kind of call is used when Redis detects that timelimit_exit is
* true, so there is more work to do, and we do it more incrementally from
* the beforeSleep() function of the event loop.
* The function can perform more or less work, depending on the "type"
* argument. It can execute a "fast cycle" or a "slow cycle". The slow
* cycle is the main way we collect expired cycles: this happens with
* the "server.hz" frequency (usually 10 hertz).
*
* Expire cycle type:
* However the slow cycle can exit for timeout, since it used too much time.
* For this reason the function is also invoked to perform a fast cycle
* at every event loop cycle, in the beforeSleep() function. The fast cycle
* will try to perform less work, but will do it much more often.
*
* The following are the details of the two expire cycles and their stop
* conditions:
*
* If type is ACTIVE_EXPIRE_CYCLE_FAST the function will try to run a
* "fast" expire cycle that takes no longer than EXPIRE_FAST_CYCLE_DURATION
* microseconds, and is not repeated again before the same amount of time.
* The cycle will also refuse to run at all if the latest slow cycle did not
* terminate because of a time limit condition.
*
* If type is ACTIVE_EXPIRE_CYCLE_SLOW, that normal expire cycle is
* executed, where the time limit is a percentage of the REDIS_HZ period
* as specified by the ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC define. */
* as specified by the ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC define. In the
* fast cycle, the check of every database is interrupted once the number
* of already expired keys in the database is estimated to be lower than
* a given percentage, in order to avoid doing too much work to gain too
* little memory.
*
* The configured expire "effort" will modify the baseline parameters in
* order to do more work in both the fast and slow expire cycles.
*/
#define ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP 20
/* Keys for each DB loop. */
#define ACTIVE_EXPIRE_CYCLE_FAST_DURATION 1000
/* Microseconds. */
#define ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 25
/* Max % of CPU to use. */
#define ACTIVE_EXPIRE_CYCLE_ACCEPTABLE_STALE 10
/* % of stale keys after which
we do extra efforts. */
void
activeExpireCycle
(
int
type
)
{
/* Adjust the running parameters according to the configured expire
* effort. The default effort is 1, and the maximum configurable effort
* is 10. */
unsigned
long
effort
=
server
.
active_expire_effort
-
1
,
/* Rescale from 0 to 9. */
config_keys_per_loop
=
ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP
+
ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP
/
4
*
effort
,
config_cycle_fast_duration
=
ACTIVE_EXPIRE_CYCLE_FAST_DURATION
+
ACTIVE_EXPIRE_CYCLE_FAST_DURATION
/
4
*
effort
,
config_cycle_slow_time_perc
=
ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC
+
2
*
effort
,
config_cycle_acceptable_stale
=
ACTIVE_EXPIRE_CYCLE_ACCEPTABLE_STALE
-
effort
;
/* This function has some global state in order to continue the work
* incrementally across calls. */
static
unsigned
int
current_db
=
0
;
/* Last DB tested. */
...
...
@@ -113,10 +152,16 @@ void activeExpireCycle(int type) {
if
(
type
==
ACTIVE_EXPIRE_CYCLE_FAST
)
{
/* Don't start a fast cycle if the previous cycle did not exit
* for time limit. Also don't repeat a fast cycle for the same period
* for time limit, unless the percentage of estimated stale keys is
* too high. Also never repeat a fast cycle for the same period
* as the fast cycle total duration itself. */
if
(
!
timelimit_exit
)
return
;
if
(
start
<
last_fast_cycle
+
ACTIVE_EXPIRE_CYCLE_FAST_DURATION
*
2
)
return
;
if
(
!
timelimit_exit
&&
server
.
stat_expired_stale_perc
<
config_cycle_acceptable_stale
)
return
;
if
(
start
<
last_fast_cycle
+
(
long
long
)
config_cycle_fast_duration
*
2
)
return
;
last_fast_cycle
=
start
;
}
...
...
@@ -130,16 +175,16 @@ void activeExpireCycle(int type) {
if
(
dbs_per_call
>
server
.
dbnum
||
timelimit_exit
)
dbs_per_call
=
server
.
dbnum
;
/* We can use at max
ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC
percentage of CPU
time
* per iteration. Since this function gets called with a frequency of
/* We can use at max
'config_cycle_slow_time_perc'
percentage of CPU
*
time
per iteration. Since this function gets called with a frequency of
* server.hz times per second, the following is the max amount of
* microseconds we can spend in this function. */
timelimit
=
1000000
*
ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC
/
server
.
hz
/
100
;
timelimit
=
config_cycle_slow_time_perc
*
1000000
/
server
.
hz
/
100
;
timelimit_exit
=
0
;
if
(
timelimit
<=
0
)
timelimit
=
1
;
if
(
type
==
ACTIVE_EXPIRE_CYCLE_FAST
)
timelimit
=
ACTIVE_EXPIRE_CYCLE_FAST_DURATION
;
/* in microseconds. */
timelimit
=
config_cycle_fast_duration
;
/* in microseconds. */
/* Accumulate some global stats as we expire keys, to have some idea
* about the number of keys that are already logically expired, but still
...
...
@@ -148,7 +193,9 @@ void activeExpireCycle(int type) {
long
total_expired
=
0
;
for
(
j
=
0
;
j
<
dbs_per_call
&&
timelimit_exit
==
0
;
j
++
)
{
int
expired
;
/* Expired and checked in a single loop. */
unsigned
long
expired
,
sampled
;
redisDb
*
db
=
server
.
db
+
(
current_db
%
server
.
dbnum
);
/* Increment the DB now so we are sure if we run out of time
...
...
@@ -172,8 +219,8 @@ void activeExpireCycle(int type) {
slots
=
dictSlots
(
db
->
expires
);
now
=
mstime
();
/* When there are less than 1% filled slots
getting random
*
keys
is expensive, so stop here waiting for better times...
/* When there are less than 1% filled slots
, sampling the key
*
space
is expensive, so stop here waiting for better times...
* The dictionary will be resized asap. */
if
(
num
&&
slots
>
DICT_HT_INITIAL_SIZE
&&
(
num
*
100
/
slots
<
1
))
break
;
...
...
@@ -181,27 +228,58 @@ void activeExpireCycle(int type) {
/* The main collection cycle. Sample random keys among keys
* with an expire set, checking for expired ones. */
expired
=
0
;
sampled
=
0
;
ttl_sum
=
0
;
ttl_samples
=
0
;
if
(
num
>
ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP
)
num
=
ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP
;
while
(
num
--
)
{
dictEntry
*
de
;
long
long
ttl
;
if
((
de
=
dictGetRandomKey
(
db
->
expires
))
==
NULL
)
break
;
ttl
=
dictGetSignedIntegerVal
(
de
)
-
now
;
if
(
activeExpireCycleTryExpire
(
db
,
de
,
now
))
expired
++
;
if
(
ttl
>
0
)
{
/* We want the average TTL of keys yet not expired. */
ttl_sum
+=
ttl
;
ttl_samples
++
;
if
(
num
>
config_keys_per_loop
)
num
=
config_keys_per_loop
;
/* Here we access the low level representation of the hash table
* for speed concerns: this makes this code coupled with dict.c,
* but it hardly changed in ten years.
*
* Note that certain places of the hash table may be empty,
* so we want also a stop condition about the number of
* buckets that we scanned. However scanning for free buckets
* is very fast: we are in the cache line scanning a sequential
* array of NULL pointers, so we can scan a lot more buckets
* than keys in the same time. */
long
max_buckets
=
num
*
20
;
long
checked_buckets
=
0
;
while
(
sampled
<
num
&&
checked_buckets
<
max_buckets
)
{
for
(
int
table
=
0
;
table
<
2
;
table
++
)
{
if
(
table
==
1
&&
!
dictIsRehashing
(
db
->
expires
))
break
;
unsigned
long
idx
=
db
->
expires_cursor
;
idx
&=
db
->
expires
->
ht
[
table
].
sizemask
;
dictEntry
*
de
=
db
->
expires
->
ht
[
table
].
table
[
idx
];
long
long
ttl
;
/* Scan the current bucket of the current table. */
checked_buckets
++
;
while
(
de
)
{
/* Get the next entry now since this entry may get
* deleted. */
dictEntry
*
e
=
de
;
de
=
de
->
next
;
ttl
=
dictGetSignedIntegerVal
(
e
)
-
now
;
if
(
activeExpireCycleTryExpire
(
db
,
e
,
now
))
expired
++
;
if
(
ttl
>
0
)
{
/* We want the average TTL of keys yet
* not expired. */
ttl_sum
+=
ttl
;
ttl_samples
++
;
}
sampled
++
;
}
}
total_sampled
++
;
db
->
expires_cursor
++
;
}
total_expired
+=
expired
;
total_sampled
+=
sampled
;
/* Update the average TTL stats for this database. */
if
(
ttl_samples
)
{
...
...
@@ -225,12 +303,14 @@ void activeExpireCycle(int type) {
break
;
}
}
/* We don't repeat the cycle if there are less than 25% of keys
* found expired in the current DB. */
}
while
(
expired
>
ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP
/
4
);
/* We don't repeat the cycle for the current database if there are
* an acceptable amount of stale keys (logically expired but yet
* not reclained). */
}
while
((
expired
*
100
/
sampled
)
>
config_cycle_acceptable_stale
);
}
elapsed
=
ustime
()
-
start
;
server
.
stat_expire_cycle_time_used
+=
elapsed
;
latencyAddSampleIfNeeded
(
"expire-cycle"
,
elapsed
/
1000
);
/* Update our estimate of keys existing but yet to be expired.
...
...
src/mkreleasehdr.sh
View file @
70469b76
...
...
@@ -3,7 +3,7 @@ GIT_SHA1=`(git show-ref --head --hash=8 2> /dev/null || echo 00000000) | head -n
GIT_DIRTY
=
`
git diff
--no-ext-diff
2> /dev/null |
wc
-l
`
BUILD_ID
=
`
uname
-n
`
"-"
`
date
+%s
`
if
[
-n
"
$SOURCE_DATE_EPOCH
"
]
;
then
BUILD_ID
=
$(
date
-u
-d
"@
$SOURCE_DATE_EPOCH
"
+%s 2>/dev/null
||
date
-u
-r
"
$SOURCE_DATE_EPOCH
"
+%s 2>/dev/null
||
date
-u
%s
)
BUILD_ID
=
$(
date
-u
-d
"@
$SOURCE_DATE_EPOCH
"
+%s 2>/dev/null
||
date
-u
-r
"
$SOURCE_DATE_EPOCH
"
+%s 2>/dev/null
||
date
-u
+
%s
)
fi
test
-f
release.h
||
touch
release.h
(
cat
release.h |
grep
SHA1 |
grep
$GIT_SHA1
)
&&
\
...
...
src/module.c
View file @
70469b76
This diff is collapsed.
Click to expand it.
src/object.c
View file @
70469b76
...
...
@@ -1210,7 +1210,7 @@ sds getMemoryDoctorReport(void) {
* is MAXMEMORY_FLAG_LRU.
* Either or both of them may be <0, in that case, nothing is set. */
int
objectSetLRUOrLFU
(
robj
*
val
,
long
long
lfu_freq
,
long
long
lru_idle
,
long
long
lru_clock
)
{
long
long
lru_clock
,
int
lru_multiplier
)
{
if
(
server
.
maxmemory_policy
&
MAXMEMORY_FLAG_LFU
)
{
if
(
lfu_freq
>=
0
)
{
serverAssert
(
lfu_freq
<=
255
);
...
...
@@ -1222,7 +1222,7 @@ int objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
* according to the LRU clock resolution this Redis
* instance was compiled with (normally 1000 ms, so the
* below statement will expand to lru_idle*1000/1000. */
lru_idle
=
lru_idle
*
1000
/
LRU_CLOCK_RESOLUTION
;
lru_idle
=
lru_idle
*
lru_multiplier
/
LRU_CLOCK_RESOLUTION
;
long
lru_abs
=
lru_clock
-
lru_idle
;
/* Absolute access time. */
/* If the LRU field underflows (since LRU it is a wrapping
* clock), the best we can do is to provide a large enough LRU
...
...
src/rax.c
View file @
70469b76
...
...
@@ -1673,6 +1673,7 @@ int raxSeek(raxIterator *it, const char *op, unsigned char *ele, size_t len) {
* node, but will be our match, representing the key "f".
*
* So in that case, we don't seek backward. */
it
->
data
=
raxGetData
(
it
->
node
);
}
else
{
if
(
gt
&&
!
raxIteratorNextStep
(
it
,
0
))
return
0
;
if
(
lt
&&
!
raxIteratorPrevStep
(
it
,
0
))
return
0
;
...
...
@@ -1791,7 +1792,7 @@ int raxCompare(raxIterator *iter, const char *op, unsigned char *key, size_t key
if
(
eq
&&
key_len
==
iter
->
key_len
)
return
1
;
else
if
(
lt
)
return
iter
->
key_len
<
key_len
;
else
if
(
gt
)
return
iter
->
key_len
>
key_len
;
return
0
;
else
return
0
;
/* Avoid warning, just 'eq' is handled before. */
}
else
if
(
cmp
>
0
)
{
return
gt
?
1
:
0
;
}
else
/* (cmp < 0) */
{
...
...
src/rdb.c
View file @
70469b76
...
...
@@ -2006,7 +2006,7 @@ void rdbLoadProgressCallback(rio *r, const void *buf, size_t len) {
/* The DB can take some non trivial amount of time to load. Update
* our cached time since it is used to create and update the last
* interaction time with clients and for other important things. */
updateCachedTime
();
updateCachedTime
(
0
);
if
(
server
.
masterhost
&&
server
.
repl_state
==
REPL_STATE_TRANSFER
)
replicationSendNewlineToMaster
();
loadingProgress
(
r
->
processed_bytes
);
...
...
@@ -2239,7 +2239,7 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
if
(
expiretime
!=
-
1
)
setExpire
(
NULL
,
db
,
key
,
expiretime
);
/* Set usage information (for eviction). */
objectSetLRUOrLFU
(
val
,
lfu_freq
,
lru_idle
,
lru_clock
);
objectSetLRUOrLFU
(
val
,
lfu_freq
,
lru_idle
,
lru_clock
,
1000
);
/* Decrement the key refcount since dbAdd() will take its
* own reference. */
...
...
src/redismodule.h
View file @
70469b76
...
...
@@ -392,6 +392,7 @@ typedef struct RedisModuleDictIter RedisModuleDictIter;
typedef
struct
RedisModuleCommandFilterCtx
RedisModuleCommandFilterCtx
;
typedef
struct
RedisModuleCommandFilter
RedisModuleCommandFilter
;
typedef
struct
RedisModuleInfoCtx
RedisModuleInfoCtx
;
typedef
struct
RedisModuleScanCursor
RedisModuleScanCursor
;
typedef
int
(
*
RedisModuleCmdFunc
)(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
);
typedef
void
(
*
RedisModuleDisconnectFunc
)(
RedisModuleCtx
*
ctx
,
RedisModuleBlockedClient
*
bc
);
...
...
@@ -409,6 +410,8 @@ typedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data);
typedef
void
(
*
RedisModuleCommandFilterFunc
)
(
RedisModuleCommandFilterCtx
*
filter
);
typedef
void
(
*
RedisModuleForkDoneHandler
)
(
int
exitcode
,
int
bysignal
,
void
*
user_data
);
typedef
void
(
*
RedisModuleInfoFunc
)(
RedisModuleInfoCtx
*
ctx
,
int
for_crash_report
);
typedef
void
(
*
RedisModuleScanCB
)(
RedisModuleCtx
*
ctx
,
RedisModuleString
*
keyname
,
RedisModuleKey
*
key
,
void
*
privdata
);
typedef
void
(
*
RedisModuleScanKeyCB
)(
RedisModuleKey
*
key
,
RedisModuleString
*
field
,
RedisModuleString
*
value
,
void
*
privdata
);
#define REDISMODULE_TYPE_METHOD_VERSION 2
typedef
struct
RedisModuleTypeMethods
{
...
...
@@ -458,6 +461,7 @@ size_t REDISMODULE_API_FUNC(RedisModule_CallReplyLength)(RedisModuleCallReply *r
RedisModuleCallReply
*
REDISMODULE_API_FUNC
(
RedisModule_CallReplyArrayElement
)(
RedisModuleCallReply
*
reply
,
size_t
idx
);
RedisModuleString
*
REDISMODULE_API_FUNC
(
RedisModule_CreateString
)(
RedisModuleCtx
*
ctx
,
const
char
*
ptr
,
size_t
len
);
RedisModuleString
*
REDISMODULE_API_FUNC
(
RedisModule_CreateStringFromLongLong
)(
RedisModuleCtx
*
ctx
,
long
long
ll
);
RedisModuleString
*
REDISMODULE_API_FUNC
(
RedisModule_CreateStringFromLongDouble
)(
RedisModuleCtx
*
ctx
,
long
double
ld
,
int
humanfriendly
);
RedisModuleString
*
REDISMODULE_API_FUNC
(
RedisModule_CreateStringFromString
)(
RedisModuleCtx
*
ctx
,
const
RedisModuleString
*
str
);
RedisModuleString
*
REDISMODULE_API_FUNC
(
RedisModule_CreateStringPrintf
)(
RedisModuleCtx
*
ctx
,
const
char
*
fmt
,
...);
void
REDISMODULE_API_FUNC
(
RedisModule_FreeString
)(
RedisModuleCtx
*
ctx
,
RedisModuleString
*
str
);
...
...
@@ -475,9 +479,11 @@ int REDISMODULE_API_FUNC(RedisModule_ReplyWithEmptyString)(RedisModuleCtx *ctx);
int
REDISMODULE_API_FUNC
(
RedisModule_ReplyWithVerbatimString
)(
RedisModuleCtx
*
ctx
,
const
char
*
buf
,
size_t
len
);
int
REDISMODULE_API_FUNC
(
RedisModule_ReplyWithNull
)(
RedisModuleCtx
*
ctx
);
int
REDISMODULE_API_FUNC
(
RedisModule_ReplyWithDouble
)(
RedisModuleCtx
*
ctx
,
double
d
);
int
REDISMODULE_API_FUNC
(
RedisModule_ReplyWithLongDouble
)(
RedisModuleCtx
*
ctx
,
long
double
d
);
int
REDISMODULE_API_FUNC
(
RedisModule_ReplyWithCallReply
)(
RedisModuleCtx
*
ctx
,
RedisModuleCallReply
*
reply
);
int
REDISMODULE_API_FUNC
(
RedisModule_StringToLongLong
)(
const
RedisModuleString
*
str
,
long
long
*
ll
);
int
REDISMODULE_API_FUNC
(
RedisModule_StringToDouble
)(
const
RedisModuleString
*
str
,
double
*
d
);
int
REDISMODULE_API_FUNC
(
RedisModule_StringToLongDouble
)(
const
RedisModuleString
*
str
,
long
double
*
d
);
void
REDISMODULE_API_FUNC
(
RedisModule_AutoMemory
)(
RedisModuleCtx
*
ctx
);
int
REDISMODULE_API_FUNC
(
RedisModule_Replicate
)(
RedisModuleCtx
*
ctx
,
const
char
*
cmdname
,
const
char
*
fmt
,
...);
int
REDISMODULE_API_FUNC
(
RedisModule_ReplicateVerbatim
)(
RedisModuleCtx
*
ctx
);
...
...
@@ -517,6 +523,7 @@ int REDISMODULE_API_FUNC(RedisModule_GetContextFlags)(RedisModuleCtx *ctx);
void
*
REDISMODULE_API_FUNC
(
RedisModule_PoolAlloc
)(
RedisModuleCtx
*
ctx
,
size_t
bytes
);
RedisModuleType
*
REDISMODULE_API_FUNC
(
RedisModule_CreateDataType
)(
RedisModuleCtx
*
ctx
,
const
char
*
name
,
int
encver
,
RedisModuleTypeMethods
*
typemethods
);
int
REDISMODULE_API_FUNC
(
RedisModule_ModuleTypeSetValue
)(
RedisModuleKey
*
key
,
RedisModuleType
*
mt
,
void
*
value
);
void
*
REDISMODULE_API_FUNC
(
RedisModule_ModuleTypeReplaceValue
)(
RedisModuleKey
*
key
,
RedisModuleType
*
mt
,
void
*
new_value
);
RedisModuleType
*
REDISMODULE_API_FUNC
(
RedisModule_ModuleTypeGetType
)(
RedisModuleKey
*
key
);
void
*
REDISMODULE_API_FUNC
(
RedisModule_ModuleTypeGetValue
)(
RedisModuleKey
*
key
);
int
REDISMODULE_API_FUNC
(
RedisModule_IsIOError
)(
RedisModuleIO
*
io
);
...
...
@@ -537,6 +544,8 @@ void REDISMODULE_API_FUNC(RedisModule_SaveFloat)(RedisModuleIO *io, float value)
float
REDISMODULE_API_FUNC
(
RedisModule_LoadFloat
)(
RedisModuleIO
*
io
);
void
REDISMODULE_API_FUNC
(
RedisModule_SaveLongDouble
)(
RedisModuleIO
*
io
,
long
double
value
);
long
double
REDISMODULE_API_FUNC
(
RedisModule_LoadLongDouble
)(
RedisModuleIO
*
io
);
void
*
REDISMODULE_API_FUNC
(
RedisModule_LoadDataTypeFromString
)(
const
RedisModuleString
*
str
,
const
RedisModuleType
*
mt
);
RedisModuleString
*
REDISMODULE_API_FUNC
(
RedisModule_SaveDataTypeToString
)(
RedisModuleCtx
*
ctx
,
void
*
data
,
const
RedisModuleType
*
mt
);
void
REDISMODULE_API_FUNC
(
RedisModule_Log
)(
RedisModuleCtx
*
ctx
,
const
char
*
level
,
const
char
*
fmt
,
...);
void
REDISMODULE_API_FUNC
(
RedisModule_LogIOError
)(
RedisModuleIO
*
io
,
const
char
*
levelstr
,
const
char
*
fmt
,
...);
void
REDISMODULE_API_FUNC
(
RedisModule__Assert
)(
const
char
*
estr
,
const
char
*
file
,
int
line
);
...
...
@@ -583,11 +592,18 @@ int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldDouble)(RedisModuleInfoCtx *ctx
int
REDISMODULE_API_FUNC
(
RedisModule_InfoAddFieldLongLong
)(
RedisModuleInfoCtx
*
ctx
,
char
*
field
,
long
long
value
);
int
REDISMODULE_API_FUNC
(
RedisModule_InfoAddFieldULongLong
)(
RedisModuleInfoCtx
*
ctx
,
char
*
field
,
unsigned
long
long
value
);
int
REDISMODULE_API_FUNC
(
RedisModule_SubscribeToServerEvent
)(
RedisModuleCtx
*
ctx
,
RedisModuleEvent
event
,
RedisModuleEventCallback
callback
);
int
REDISMODULE_API_FUNC
(
RedisModule_SetLRUOrLFU
)(
RedisModuleKey
*
key
,
long
long
lfu_freq
,
long
long
lru_idle
);
int
REDISMODULE_API_FUNC
(
RedisModule_GetLRUOrLFU
)(
RedisModuleKey
*
key
,
long
long
*
lfu_freq
,
long
long
*
lru_idle
);
int
REDISMODULE_API_FUNC
(
RedisModule_SetLRU
)(
RedisModuleKey
*
key
,
mstime_t
lru_idle
);
int
REDISMODULE_API_FUNC
(
RedisModule_GetLRU
)(
RedisModuleKey
*
key
,
mstime_t
*
lru_idle
);
int
REDISMODULE_API_FUNC
(
RedisModule_SetLFU
)(
RedisModuleKey
*
key
,
long
long
lfu_freq
);
int
REDISMODULE_API_FUNC
(
RedisModule_GetLFU
)(
RedisModuleKey
*
key
,
long
long
*
lfu_freq
);
RedisModuleBlockedClient
*
REDISMODULE_API_FUNC
(
RedisModule_BlockClientOnKeys
)(
RedisModuleCtx
*
ctx
,
RedisModuleCmdFunc
reply_callback
,
RedisModuleCmdFunc
timeout_callback
,
void
(
*
free_privdata
)(
RedisModuleCtx
*
,
void
*
),
long
long
timeout_ms
,
RedisModuleString
**
keys
,
int
numkeys
,
void
*
privdata
);
void
REDISMODULE_API_FUNC
(
RedisModule_SignalKeyAsReady
)(
RedisModuleCtx
*
ctx
,
RedisModuleString
*
key
);
RedisModuleString
*
REDISMODULE_API_FUNC
(
RedisModule_GetBlockedClientReadyKey
)(
RedisModuleCtx
*
ctx
);
RedisModuleScanCursor
*
REDISMODULE_API_FUNC
(
RedisModule_ScanCursorCreate
)();
void
REDISMODULE_API_FUNC
(
RedisModule_ScanCursorRestart
)(
RedisModuleScanCursor
*
cursor
);
void
REDISMODULE_API_FUNC
(
RedisModule_ScanCursorDestroy
)(
RedisModuleScanCursor
*
cursor
);
int
REDISMODULE_API_FUNC
(
RedisModule_Scan
)(
RedisModuleCtx
*
ctx
,
RedisModuleScanCursor
*
cursor
,
RedisModuleScanCB
fn
,
void
*
privdata
);
int
REDISMODULE_API_FUNC
(
RedisModule_ScanKey
)(
RedisModuleKey
*
key
,
RedisModuleScanCursor
*
cursor
,
RedisModuleScanKeyCB
fn
,
void
*
privdata
);
/* Experimental APIs */
#ifdef REDISMODULE_EXPERIMENTAL_API
...
...
@@ -668,6 +684,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API
(
ReplyWithNull
);
REDISMODULE_GET_API
(
ReplyWithCallReply
);
REDISMODULE_GET_API
(
ReplyWithDouble
);
REDISMODULE_GET_API
(
ReplyWithLongDouble
);
REDISMODULE_GET_API
(
GetSelectedDb
);
REDISMODULE_GET_API
(
SelectDb
);
REDISMODULE_GET_API
(
OpenKey
);
...
...
@@ -678,6 +695,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API
(
ListPop
);
REDISMODULE_GET_API
(
StringToLongLong
);
REDISMODULE_GET_API
(
StringToDouble
);
REDISMODULE_GET_API
(
StringToLongDouble
);
REDISMODULE_GET_API
(
Call
);
REDISMODULE_GET_API
(
CallReplyProto
);
REDISMODULE_GET_API
(
FreeCallReply
);
...
...
@@ -689,6 +707,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API
(
CreateStringFromCallReply
);
REDISMODULE_GET_API
(
CreateString
);
REDISMODULE_GET_API
(
CreateStringFromLongLong
);
REDISMODULE_GET_API
(
CreateStringFromLongDouble
);
REDISMODULE_GET_API
(
CreateStringFromString
);
REDISMODULE_GET_API
(
CreateStringPrintf
);
REDISMODULE_GET_API
(
FreeString
);
...
...
@@ -728,6 +747,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API
(
PoolAlloc
);
REDISMODULE_GET_API
(
CreateDataType
);
REDISMODULE_GET_API
(
ModuleTypeSetValue
);
REDISMODULE_GET_API
(
ModuleTypeReplaceValue
);
REDISMODULE_GET_API
(
ModuleTypeGetType
);
REDISMODULE_GET_API
(
ModuleTypeGetValue
);
REDISMODULE_GET_API
(
IsIOError
);
...
...
@@ -747,6 +767,8 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API
(
LoadFloat
);
REDISMODULE_GET_API
(
SaveLongDouble
);
REDISMODULE_GET_API
(
LoadLongDouble
);
REDISMODULE_GET_API
(
SaveDataTypeToString
);
REDISMODULE_GET_API
(
LoadDataTypeFromString
);
REDISMODULE_GET_API
(
EmitAOF
);
REDISMODULE_GET_API
(
Log
);
REDISMODULE_GET_API
(
LogIOError
);
...
...
@@ -796,11 +818,18 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API
(
GetClientInfoById
);
REDISMODULE_GET_API
(
PublishMessage
);
REDISMODULE_GET_API
(
SubscribeToServerEvent
);
REDISMODULE_GET_API
(
SetLRUOrLFU
);
REDISMODULE_GET_API
(
GetLRUOrLFU
);
REDISMODULE_GET_API
(
SetLRU
);
REDISMODULE_GET_API
(
GetLRU
);
REDISMODULE_GET_API
(
SetLFU
);
REDISMODULE_GET_API
(
GetLFU
);
REDISMODULE_GET_API
(
BlockClientOnKeys
);
REDISMODULE_GET_API
(
SignalKeyAsReady
);
REDISMODULE_GET_API
(
GetBlockedClientReadyKey
);
REDISMODULE_GET_API
(
ScanCursorCreate
);
REDISMODULE_GET_API
(
ScanCursorRestart
);
REDISMODULE_GET_API
(
ScanCursorDestroy
);
REDISMODULE_GET_API
(
Scan
);
REDISMODULE_GET_API
(
ScanKey
);
#ifdef REDISMODULE_EXPERIMENTAL_API
REDISMODULE_GET_API
(
GetThreadSafeContext
);
...
...
src/sentinel.c
View file @
70469b76
...
...
@@ -3993,11 +3993,14 @@ int sentinelSendSlaveOf(sentinelRedisInstance *ri, char *host, int port) {
* an issue because CLIENT is variadic command, so Redis will not
* recognized as a syntax error, and the transaction will not fail (but
* only the unsupported command will fail). */
retval
=
redisAsyncCommand
(
ri
->
link
->
cc
,
sentinelDiscardReplyCallback
,
ri
,
"%s KILL TYPE normal"
,
sentinelInstanceMapCommand
(
ri
,
"CLIENT"
));
if
(
retval
==
C_ERR
)
return
retval
;
ri
->
link
->
pending_commands
++
;
for
(
int
type
=
0
;
type
<
2
;
type
++
)
{
retval
=
redisAsyncCommand
(
ri
->
link
->
cc
,
sentinelDiscardReplyCallback
,
ri
,
"%s KILL TYPE %s"
,
sentinelInstanceMapCommand
(
ri
,
"CLIENT"
),
type
==
0
?
"normal"
:
"pubsub"
);
if
(
retval
==
C_ERR
)
return
retval
;
ri
->
link
->
pending_commands
++
;
}
retval
=
redisAsyncCommand
(
ri
->
link
->
cc
,
sentinelDiscardReplyCallback
,
ri
,
"%s"
,
...
...
src/server.c
View file @
70469b76
...
...
@@ -1691,8 +1691,7 @@ void databasesCron(void) {
}
/* Defrag keys gradually. */
if
(
server
.
active_defrag_enabled
)
activeDefragCycle
();
activeDefragCycle
();
/* Perform hash tables rehashing if needed, but only if there are no
* other processes saving the DB on disk. Otherwise rehashing is bad
...
...
@@ -1736,20 +1735,29 @@ void databasesCron(void) {
/* We take a cached value of the unix time in the global state because with
* virtual memory and aging there is to store the current time in objects at
* every object access, and accuracy is not needed. To access a global var is
* a lot faster than calling time(NULL) */
void
updateCachedTime
(
void
)
{
server
.
unixtime
=
time
(
NULL
);
server
.
mstime
=
mstime
();
* a lot faster than calling time(NULL).
*
* This function should be fast because it is called at every command execution
* in call(), so it is possible to decide if to update the daylight saving
* info or not using the 'update_daylight_info' argument. Normally we update
* such info only when calling this function from serverCron() but not when
* calling it from call(). */
void
updateCachedTime
(
int
update_daylight_info
)
{
server
.
ustime
=
ustime
();
server
.
mstime
=
server
.
ustime
/
1000
;
server
.
unixtime
=
server
.
mstime
/
1000
;
/* To get information about daylight saving time, we need to call
* localtime_r and cache the result. However calling localtime_r in this
* context is safe since we will never fork() while here, in the main
* thread. The logging function will call a thread safe version of
* localtime that has no locks. */
struct
tm
tm
;
time_t
ut
=
server
.
unixtime
;
localtime_r
(
&
ut
,
&
tm
);
server
.
daylight_active
=
tm
.
tm_isdst
;
if
(
update_daylight_info
)
{
struct
tm
tm
;
time_t
ut
=
server
.
unixtime
;
localtime_r
(
&
ut
,
&
tm
);
server
.
daylight_active
=
tm
.
tm_isdst
;
}
}
void
checkChildrenDone
(
void
)
{
...
...
@@ -1838,7 +1846,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
if
(
server
.
watchdog_period
)
watchdogScheduleSignal
(
server
.
watchdog_period
);
/* Update the time cache. */
updateCachedTime
();
updateCachedTime
(
1
);
server
.
hz
=
server
.
config_hz
;
/* Adapt the server.hz value to the number of configured clients. If we have
...
...
@@ -2258,7 +2266,7 @@ void createSharedObjects(void) {
void
initServerConfig
(
void
)
{
int
j
;
updateCachedTime
();
updateCachedTime
(
1
);
getRandomHexChars
(
server
.
runid
,
CONFIG_RUN_ID_SIZE
);
server
.
runid
[
CONFIG_RUN_ID_SIZE
]
=
'\0'
;
changeReplicationId
();
...
...
@@ -2285,6 +2293,7 @@ void initServerConfig(void) {
server
.
maxidletime
=
CONFIG_DEFAULT_CLIENT_TIMEOUT
;
server
.
tcpkeepalive
=
CONFIG_DEFAULT_TCP_KEEPALIVE
;
server
.
active_expire_enabled
=
1
;
server
.
active_expire_effort
=
CONFIG_DEFAULT_ACTIVE_EXPIRE_EFFORT
;
server
.
jemalloc_bg_thread
=
1
;
server
.
active_defrag_enabled
=
CONFIG_DEFAULT_ACTIVE_DEFRAG
;
server
.
active_defrag_ignore_bytes
=
CONFIG_DEFAULT_DEFRAG_IGNORE_BYTES
;
...
...
@@ -2736,6 +2745,7 @@ void resetServerStats(void) {
server
.
stat_expiredkeys
=
0
;
server
.
stat_expired_stale_perc
=
0
;
server
.
stat_expired_time_cap_reached_count
=
0
;
server
.
stat_expire_cycle_time_used
=
0
;
server
.
stat_evictedkeys
=
0
;
server
.
stat_keyspace_misses
=
0
;
server
.
stat_keyspace_hits
=
0
;
...
...
@@ -2777,6 +2787,7 @@ void initServer(void) {
server
.
hz
=
server
.
config_hz
;
server
.
pid
=
getpid
();
server
.
current_client
=
NULL
;
server
.
fixed_time_expire
=
0
;
server
.
clients
=
listCreate
();
server
.
clients_index
=
raxNew
();
server
.
clients_to_close
=
listCreate
();
...
...
@@ -2838,12 +2849,14 @@ void initServer(void) {
for
(
j
=
0
;
j
<
server
.
dbnum
;
j
++
)
{
server
.
db
[
j
].
dict
=
dictCreate
(
&
dbDictType
,
NULL
);
server
.
db
[
j
].
expires
=
dictCreate
(
&
keyptrDictType
,
NULL
);
server
.
db
[
j
].
expires_cursor
=
0
;
server
.
db
[
j
].
blocking_keys
=
dictCreate
(
&
keylistDictType
,
NULL
);
server
.
db
[
j
].
ready_keys
=
dictCreate
(
&
objectKeyPointerValueDictType
,
NULL
);
server
.
db
[
j
].
watched_keys
=
dictCreate
(
&
keylistDictType
,
NULL
);
server
.
db
[
j
].
id
=
j
;
server
.
db
[
j
].
avg_ttl
=
0
;
server
.
db
[
j
].
defrag_later
=
listCreate
();
listSetFreeMethod
(
server
.
db
[
j
].
defrag_later
,(
void
(
*
)(
void
*
))
sdsfree
);
}
evictionPoolAlloc
();
/* Initialize the LRU keys pool. */
server
.
pubsub_channels
=
dictCreate
(
&
keylistDictType
,
NULL
);
...
...
@@ -3244,10 +3257,13 @@ void preventCommandReplication(client *c) {
*
*/
void
call
(
client
*
c
,
int
flags
)
{
long
long
dirty
,
start
,
duration
;
long
long
dirty
;
ustime_t
start
,
duration
;
int
client_old_flags
=
c
->
flags
;
struct
redisCommand
*
real_cmd
=
c
->
cmd
;
server
.
fixed_time_expire
++
;
/* Sent the command to clients in MONITOR mode, only if the commands are
* not generated from reading an AOF. */
if
(
listLength
(
server
.
monitors
)
&&
...
...
@@ -3265,7 +3281,8 @@ void call(client *c, int flags) {
/* Call the command. */
dirty
=
server
.
dirty
;
start
=
ustime
();
updateCachedTime
(
0
);
start
=
server
.
ustime
;
c
->
cmd
->
proc
(
c
);
duration
=
ustime
()
-
start
;
dirty
=
server
.
dirty
-
dirty
;
...
...
@@ -3372,6 +3389,7 @@ void call(client *c, int flags) {
trackingRememberKeys
(
caller
);
}
server
.
fixed_time_expire
--
;
server
.
stat_numcommands
++
;
}
...
...
@@ -4253,6 +4271,7 @@ sds genRedisInfoString(char *section) {
"expired_keys:%lld
\r\n
"
"expired_stale_perc:%.2f
\r\n
"
"expired_time_cap_reached_count:%lld
\r\n
"
"expire_cycle_cpu_milliseconds:%lld
\r\n
"
"evicted_keys:%lld
\r\n
"
"keyspace_hits:%lld
\r\n
"
"keyspace_misses:%lld
\r\n
"
...
...
@@ -4280,6 +4299,7 @@ sds genRedisInfoString(char *section) {
server
.
stat_expiredkeys
,
server
.
stat_expired_stale_perc
*
100
,
server
.
stat_expired_time_cap_reached_count
,
server
.
stat_expire_cycle_time_used
/
1000
,
server
.
stat_evictedkeys
,
server
.
stat_keyspace_hits
,
server
.
stat_keyspace_misses
,
...
...
src/server.h
View file @
70469b76
...
...
@@ -50,6 +50,7 @@
#include <signal.h>
typedef
long
long
mstime_t
;
/* millisecond time type. */
typedef
long
long
ustime_t
;
/* microsecond time type. */
#include "ae.h"
/* Event driven programming library */
#include "sds.h"
/* Dynamic safe strings */
...
...
@@ -173,15 +174,13 @@ typedef long long mstime_t; /* millisecond time type. */
#define CONFIG_DEFAULT_DEFRAG_THRESHOLD_LOWER 10
/* don't defrag when fragmentation is below 10% */
#define CONFIG_DEFAULT_DEFRAG_THRESHOLD_UPPER 100
/* maximum defrag force at 100% fragmentation */
#define CONFIG_DEFAULT_DEFRAG_IGNORE_BYTES (100<<20)
/* don't defrag if frag overhead is below 100mb */
#define CONFIG_DEFAULT_DEFRAG_CYCLE_MIN
5
/*
5
% CPU min (at lower threshold) */
#define CONFIG_DEFAULT_DEFRAG_CYCLE_MAX
7
5
/*
7
5% CPU max (at upper threshold) */
#define CONFIG_DEFAULT_DEFRAG_CYCLE_MIN
1
/*
1
% CPU min (at lower threshold) */
#define CONFIG_DEFAULT_DEFRAG_CYCLE_MAX
2
5
/*
2
5% CPU max (at upper threshold) */
#define CONFIG_DEFAULT_DEFRAG_MAX_SCAN_FIELDS 1000
/* keys with more than 1000 fields will be processed separately */
#define CONFIG_DEFAULT_PROTO_MAX_BULK_LEN (512ll*1024*1024)
/* Bulk request max size */
#define CONFIG_DEFAULT_TRACKING_TABLE_MAX_FILL 10
/* 10% tracking table max fill. */
#define CONFIG_DEFAULT_ACTIVE_EXPIRE_EFFORT 1
/* From 1 to 10. */
#define ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 20
/* Loopkups per loop. */
#define ACTIVE_EXPIRE_CYCLE_FAST_DURATION 1000
/* Microseconds */
#define ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 25
/* CPU max % for keys collection */
#define ACTIVE_EXPIRE_CYCLE_SLOW 0
#define ACTIVE_EXPIRE_CYCLE_FAST 1
...
...
@@ -720,6 +719,7 @@ typedef struct redisDb {
dict
*
watched_keys
;
/* WATCHED keys for MULTI/EXEC CAS */
int
id
;
/* Database ID */
long
long
avg_ttl
;
/* Average TTL, just for stats */
unsigned
long
expires_cursor
;
/* Cursor of the active expire cycle. */
list
*
defrag_later
;
/* List of key names to attempt to defrag one by one, gradually. */
}
redisDb
;
...
...
@@ -1133,7 +1133,8 @@ struct redisServer {
list
*
clients_pending_write
;
/* There is to write or install handler. */
list
*
clients_pending_read
;
/* Client has pending read socket buffers. */
list
*
slaves
,
*
monitors
;
/* List of slaves and MONITORs */
client
*
current_client
;
/* Current client, only used on crash report */
client
*
current_client
;
/* Current client executing the command. */
long
fixed_time_expire
;
/* If > 0, expire keys against server.mstime. */
rax
*
clients_index
;
/* Active clients dictionary by client ID. */
int
clients_paused
;
/* True if clients are currently paused */
mstime_t
clients_pause_end_time
;
/* Time when we undo clients_paused */
...
...
@@ -1165,6 +1166,7 @@ struct redisServer {
long
long
stat_expiredkeys
;
/* Number of expired keys */
double
stat_expired_stale_perc
;
/* Percentage of keys probably expired */
long
long
stat_expired_time_cap_reached_count
;
/* Early expire cylce stops.*/
long
long
stat_expire_cycle_time_used
;
/* Cumulative microseconds used. */
long
long
stat_evictedkeys
;
/* Number of evicted keys (maxmemory) */
long
long
stat_keyspace_hits
;
/* Number of successful lookups of keys */
long
long
stat_keyspace_misses
;
/* Number of failed lookups of keys */
...
...
@@ -1203,6 +1205,7 @@ struct redisServer {
int
maxidletime
;
/* Client timeout in seconds */
int
tcpkeepalive
;
/* Set SO_KEEPALIVE if non-zero. */
int
active_expire_enabled
;
/* Can be disabled for testing purposes. */
int
active_expire_effort
;
/* From 1 (default) to 10, active effort. */
int
active_defrag_enabled
;
int
jemalloc_bg_thread
;
/* Enable jemalloc background thread */
size_t
active_defrag_ignore_bytes
;
/* minimum amount of fragmentation waste to start active defrag */
...
...
@@ -1400,7 +1403,8 @@ struct redisServer {
_Atomic
time_t
unixtime
;
/* Unix time sampled every cron cycle. */
time_t
timezone
;
/* Cached timezone. As set by tzset(). */
int
daylight_active
;
/* Currently in daylight saving time. */
long
long
mstime
;
/* 'unixtime' with milliseconds resolution. */
mstime_t
mstime
;
/* 'unixtime' in milliseconds. */
ustime_t
ustime
;
/* 'unixtime' in microseconds. */
/* Pubsub */
dict
*
pubsub_channels
;
/* Map channels to list of subscribed clients */
list
*
pubsub_patterns
;
/* A list of pubsub_patterns */
...
...
@@ -1999,7 +2003,7 @@ void populateCommandTable(void);
void
resetCommandTableStats
(
void
);
void
adjustOpenFilesLimit
(
void
);
void
closeListeningSockets
(
int
unlink_unix_socket
);
void
updateCachedTime
(
void
);
void
updateCachedTime
(
int
update_daylight_info
);
void
resetServerStats
(
void
);
void
activeDefragCycle
(
void
);
unsigned
int
getLRUClock
(
void
);
...
...
@@ -2089,7 +2093,7 @@ robj *lookupKeyWriteWithFlags(redisDb *db, robj *key, int flags);
robj
*
objectCommandLookup
(
client
*
c
,
robj
*
key
);
robj
*
objectCommandLookupOrReply
(
client
*
c
,
robj
*
key
,
robj
*
reply
);
int
objectSetLRUOrLFU
(
robj
*
val
,
long
long
lfu_freq
,
long
long
lru_idle
,
long
long
lru_clock
);
long
long
lru_clock
,
int
lru_multiplier
);
#define LOOKUP_NONE 0
#define LOOKUP_NOTOUCH (1<<0)
void
dbAdd
(
redisDb
*
db
,
robj
*
key
,
robj
*
val
);
...
...
src/t_stream.c
View file @
70469b76
...
...
@@ -1220,6 +1220,14 @@ void xaddCommand(client *c) {
return
;
}
/* Return ASAP if minimal ID (0-0) was given so we avoid possibly creating
* a new stream and have streamAppendItem fail, leaving an empty key in the
* database. */
if
(
id_given
&&
id
.
ms
==
0
&&
id
.
seq
==
0
)
{
addReplyError
(
c
,
"The ID specified in XADD must be greater than 0-0"
);
return
;
}
/* Lookup the stream at key. */
robj
*
o
;
stream
*
s
;
...
...
Prev
1
2
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