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
0cdafe3c
Commit
0cdafe3c
authored
May 16, 2023
by
Vitaly Arbuzov
Browse files
Merge branch 'unstable' into dict-split-by-slot
parents
13373654
e4527288
Changes
10
Hide whitespace changes
Inline
Side-by-side
src/latency.c
View file @
0cdafe3c
...
@@ -726,3 +726,14 @@ nodataerr:
...
@@ -726,3 +726,14 @@ nodataerr:
"No samples available for event '%s'"
,
(
char
*
)
c
->
argv
[
2
]
->
ptr
);
"No samples available for event '%s'"
,
(
char
*
)
c
->
argv
[
2
]
->
ptr
);
}
}
void
durationAddSample
(
int
type
,
monotime
duration
)
{
if
(
type
>=
EL_DURATION_TYPE_NUM
)
{
return
;
}
durationStats
*
ds
=
&
server
.
duration_stats
[
type
];
ds
->
cnt
++
;
ds
->
sum
+=
duration
;
if
(
duration
>
ds
->
max
)
{
ds
->
max
=
duration
;
}
}
src/latency.h
View file @
0cdafe3c
...
@@ -89,4 +89,20 @@ void latencyAddSample(const char *event, mstime_t latency);
...
@@ -89,4 +89,20 @@ void latencyAddSample(const char *event, mstime_t latency);
#define latencyRemoveNestedEvent(event_var,nested_var) \
#define latencyRemoveNestedEvent(event_var,nested_var) \
event_var += nested_var;
event_var += nested_var;
typedef
struct
durationStats
{
unsigned
long
long
cnt
;
unsigned
long
long
sum
;
unsigned
long
long
max
;
}
durationStats
;
typedef
enum
{
EL_DURATION_TYPE_EL
=
0
,
// cumulative time duration metric of the whole eventloop
EL_DURATION_TYPE_CMD
,
// cumulative time duration metric of executing commands
EL_DURATION_TYPE_AOF
,
// cumulative time duration metric of flushing AOF in eventloop
EL_DURATION_TYPE_CRON
,
// cumulative time duration metric of cron (serverCron and beforeSleep, but excluding IO and AOF)
EL_DURATION_TYPE_NUM
}
DurationType
;
void
durationAddSample
(
int
type
,
monotime
duration
);
#endif
/* __LATENCY_H */
#endif
/* __LATENCY_H */
src/module.c
View file @
0cdafe3c
...
@@ -11702,7 +11702,7 @@ void moduleNotifyKeyUnlink(robj *key, robj *val, int dbid, int flags) {
...
@@ -11702,7 +11702,7 @@ void moduleNotifyKeyUnlink(robj *key, robj *val, int dbid, int flags) {
} else if (flags & DB_FLAG_KEY_OVERWRITE) {
} else if (flags & DB_FLAG_KEY_OVERWRITE) {
subevent = REDISMODULE_SUBEVENT_KEY_OVERWRITTEN;
subevent = REDISMODULE_SUBEVENT_KEY_OVERWRITTEN;
}
}
KeyInfo info = {dbid, key, val, REDISMODULE_
WRITE
};
KeyInfo info = {dbid, key, val, REDISMODULE_
READ
};
moduleFireServerEvent(REDISMODULE_EVENT_KEY, subevent, &info);
moduleFireServerEvent(REDISMODULE_EVENT_KEY, subevent, &info);
if (val->type == OBJ_MODULE) {
if (val->type == OBJ_MODULE) {
...
...
src/redis-cli.c
View file @
0cdafe3c
...
@@ -239,6 +239,7 @@ static struct config {
...
@@ -239,6 +239,7 @@ static struct config {
int
get_functions_rdb_mode
;
int
get_functions_rdb_mode
;
int
stat_mode
;
int
stat_mode
;
int
scan_mode
;
int
scan_mode
;
int
count
;
int
intrinsic_latency_mode
;
int
intrinsic_latency_mode
;
int
intrinsic_latency_duration
;
int
intrinsic_latency_duration
;
sds
pattern
;
sds
pattern
;
...
@@ -2727,6 +2728,8 @@ static int parseOptions(int argc, char **argv) {
...
@@ -2727,6 +2728,8 @@ static int parseOptions(int argc, char **argv) {
}
else
if
(
!
strcmp
(
argv
[
i
],
"--pattern"
)
&&
!
lastarg
)
{
}
else
if
(
!
strcmp
(
argv
[
i
],
"--pattern"
)
&&
!
lastarg
)
{
sdsfree
(
config
.
pattern
);
sdsfree
(
config
.
pattern
);
config
.
pattern
=
sdsnew
(
argv
[
++
i
]);
config
.
pattern
=
sdsnew
(
argv
[
++
i
]);
}
else
if
(
!
strcmp
(
argv
[
i
],
"--count"
)
&&
!
lastarg
)
{
config
.
count
=
atoi
(
argv
[
++
i
]);
}
else
if
(
!
strcmp
(
argv
[
i
],
"--quoted-pattern"
)
&&
!
lastarg
)
{
}
else
if
(
!
strcmp
(
argv
[
i
],
"--quoted-pattern"
)
&&
!
lastarg
)
{
sdsfree
(
config
.
pattern
);
sdsfree
(
config
.
pattern
);
config
.
pattern
=
unquoteCString
(
argv
[
++
i
]);
config
.
pattern
=
unquoteCString
(
argv
[
++
i
]);
...
@@ -3081,6 +3084,7 @@ version,tls_usage);
...
@@ -3081,6 +3084,7 @@ version,tls_usage);
" --scan List all keys using the SCAN command.
\n
"
" --scan List all keys using the SCAN command.
\n
"
" --pattern <pat> Keys pattern when using the --scan, --bigkeys or --hotkeys
\n
"
" --pattern <pat> Keys pattern when using the --scan, --bigkeys or --hotkeys
\n
"
" options (default: *).
\n
"
" options (default: *).
\n
"
" --count <count> Count option when using the --scan, --bigkeys or --hotkeys (default: 10).
\n
"
" --quoted-pattern <pat> Same as --pattern, but the specified string can be
\n
"
" --quoted-pattern <pat> Same as --pattern, but the specified string can be
\n
"
" quoted, in order to pass an otherwise non binary-safe string.
\n
"
" quoted, in order to pass an otherwise non binary-safe string.
\n
"
" --intrinsic-latency <sec> Run a test to measure intrinsic system latency.
\n
"
" --intrinsic-latency <sec> Run a test to measure intrinsic system latency.
\n
"
...
@@ -3111,6 +3115,7 @@ version,tls_usage);
...
@@ -3111,6 +3115,7 @@ version,tls_usage);
" redis-cli --quoted-input set '
\"
null-
\\
x00-separated
\"
' value
\n
"
" redis-cli --quoted-input set '
\"
null-
\\
x00-separated
\"
' value
\n
"
" redis-cli --eval myscript.lua key1 key2 , arg1 arg2 arg3
\n
"
" redis-cli --eval myscript.lua key1 key2 , arg1 arg2 arg3
\n
"
" redis-cli --scan --pattern '*:12345*'
\n
"
" redis-cli --scan --pattern '*:12345*'
\n
"
" redis-cli --scan --pattern '*:12345*' --count 100
\n
"
"
\n
"
"
\n
"
" (Note: when using --eval the comma separates KEYS[] from ARGV[] items)
\n
"
" (Note: when using --eval the comma separates KEYS[] from ARGV[] items)
\n
"
"
\n
"
"
\n
"
...
@@ -8826,8 +8831,8 @@ static redisReply *sendScan(unsigned long long *it) {
...
@@ -8826,8 +8831,8 @@ static redisReply *sendScan(unsigned long long *it) {
redisReply
*
reply
;
redisReply
*
reply
;
if
(
config
.
pattern
)
if
(
config
.
pattern
)
reply
=
redisCommand
(
context
,
"SCAN %llu MATCH %b"
,
reply
=
redisCommand
(
context
,
"SCAN %llu MATCH %b
COUNT %d
"
,
*
it
,
config
.
pattern
,
sdslen
(
config
.
pattern
));
*
it
,
config
.
pattern
,
sdslen
(
config
.
pattern
)
,
config
.
count
);
else
else
reply
=
redisCommand
(
context
,
"SCAN %llu"
,
*
it
);
reply
=
redisCommand
(
context
,
"SCAN %llu"
,
*
it
);
...
@@ -9770,6 +9775,7 @@ int main(int argc, char **argv) {
...
@@ -9770,6 +9775,7 @@ int main(int argc, char **argv) {
config
.
get_functions_rdb_mode
=
0
;
config
.
get_functions_rdb_mode
=
0
;
config
.
stat_mode
=
0
;
config
.
stat_mode
=
0
;
config
.
scan_mode
=
0
;
config
.
scan_mode
=
0
;
config
.
count
=
10
;
config
.
intrinsic_latency_mode
=
0
;
config
.
intrinsic_latency_mode
=
0
;
config
.
pattern
=
NULL
;
config
.
pattern
=
NULL
;
config
.
rdb_filename
=
NULL
;
config
.
rdb_filename
=
NULL
;
...
...
src/server.c
View file @
0cdafe3c
...
@@ -717,22 +717,24 @@ int allPersistenceDisabled(void) {
...
@@ -717,22 +717,24 @@ int allPersistenceDisabled(void) {
/* ======================= Cron: called every 100 ms ======================== */
/* ======================= Cron: called every 100 ms ======================== */
/* Add a sample to the operations per second array of samples. */
/* Add a sample to the instantaneous metric. This function computes the quotient
void
trackInstantaneousMetric
(
int
metric
,
long
long
current_reading
)
{
* of the increment of value and base, which is useful to record operation count
long
long
now
=
mstime
();
* per second, or the average time consumption of an operation.
long
long
t
=
now
-
server
.
inst_metric
[
metric
].
last_sample_time
;
*
long
long
ops
=
current_reading
-
* current_value - The dividend
server
.
inst_metric
[
metric
].
last_sample_count
;
* current_base - The divisor
long
long
ops_sec
;
* */
void
trackInstantaneousMetric
(
int
metric
,
long
long
current_value
,
long
long
current_base
,
long
long
factor
)
{
ops_sec
=
t
>
0
?
(
ops
*
1000
/
t
)
:
0
;
if
(
server
.
inst_metric
[
metric
].
last_sample_base
>
0
)
{
long
long
base
=
current_base
-
server
.
inst_metric
[
metric
].
last_sample_base
;
server
.
inst_metric
[
metric
].
samples
[
server
.
inst_metric
[
metric
].
idx
]
=
long
long
value
=
current_value
-
server
.
inst_metric
[
metric
].
last_sample_value
;
ops_sec
;
long
long
avg
=
base
>
0
?
(
value
*
factor
/
base
)
:
0
;
server
.
inst_metric
[
metric
].
idx
++
;
server
.
inst_metric
[
metric
].
samples
[
server
.
inst_metric
[
metric
].
idx
]
=
avg
;
server
.
inst_metric
[
metric
].
idx
%=
STATS_METRIC_SAMPLES
;
server
.
inst_metric
[
metric
].
idx
++
;
server
.
inst_metric
[
metric
].
last_sample_time
=
now
;
server
.
inst_metric
[
metric
].
idx
%=
STATS_METRIC_SAMPLES
;
server
.
inst_metric
[
metric
].
last_sample_count
=
current_reading
;
}
server
.
inst_metric
[
metric
].
last_sample_base
=
current_base
;
server
.
inst_metric
[
metric
].
last_sample_value
=
current_value
;
}
}
/* Return the mean of all the samples. */
/* Return the mean of all the samples. */
...
@@ -1308,6 +1310,8 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
...
@@ -1308,6 +1310,8 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
/* for debug purposes: skip actual cron work if pause_cron is on */
/* for debug purposes: skip actual cron work if pause_cron is on */
if
(
server
.
pause_cron
)
return
1000
/
server
.
hz
;
if
(
server
.
pause_cron
)
return
1000
/
server
.
hz
;
monotime
cron_start
=
getMonotonicUs
();
run_with_period
(
100
)
{
run_with_period
(
100
)
{
long
long
stat_net_input_bytes
,
stat_net_output_bytes
;
long
long
stat_net_input_bytes
,
stat_net_output_bytes
;
long
long
stat_net_repl_input_bytes
,
stat_net_repl_output_bytes
;
long
long
stat_net_repl_input_bytes
,
stat_net_repl_output_bytes
;
...
@@ -1315,16 +1319,21 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
...
@@ -1315,16 +1319,21 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
atomicGet
(
server
.
stat_net_output_bytes
,
stat_net_output_bytes
);
atomicGet
(
server
.
stat_net_output_bytes
,
stat_net_output_bytes
);
atomicGet
(
server
.
stat_net_repl_input_bytes
,
stat_net_repl_input_bytes
);
atomicGet
(
server
.
stat_net_repl_input_bytes
,
stat_net_repl_input_bytes
);
atomicGet
(
server
.
stat_net_repl_output_bytes
,
stat_net_repl_output_bytes
);
atomicGet
(
server
.
stat_net_repl_output_bytes
,
stat_net_repl_output_bytes
);
monotime
current_time
=
getMonotonicUs
();
trackInstantaneousMetric
(
STATS_METRIC_COMMAND
,
server
.
stat_numcommands
);
long
long
factor
=
1000000
;
// us
trackInstantaneousMetric
(
STATS_METRIC_NET_INPUT
,
trackInstantaneousMetric
(
STATS_METRIC_COMMAND
,
server
.
stat_numcommands
,
current_time
,
factor
);
stat_net_input_bytes
+
stat_net_repl_input_bytes
);
trackInstantaneousMetric
(
STATS_METRIC_NET_INPUT
,
stat_net_input_bytes
+
stat_net_repl_input_bytes
,
trackInstantaneousMetric
(
STATS_METRIC_NET_OUTPUT
,
current_time
,
factor
);
stat_net_output_bytes
+
stat_net_repl_output_bytes
);
trackInstantaneousMetric
(
STATS_METRIC_NET_OUTPUT
,
stat_net_output_bytes
+
stat_net_repl_output_bytes
,
trackInstantaneousMetric
(
STATS_METRIC_NET_INPUT_REPLICATION
,
current_time
,
factor
);
stat_net_repl_input_bytes
);
trackInstantaneousMetric
(
STATS_METRIC_NET_INPUT_REPLICATION
,
stat_net_repl_input_bytes
,
current_time
,
trackInstantaneousMetric
(
STATS_METRIC_NET_OUTPUT_REPLICATION
,
factor
);
stat_net_repl_output_bytes
);
trackInstantaneousMetric
(
STATS_METRIC_NET_OUTPUT_REPLICATION
,
stat_net_repl_output_bytes
,
current_time
,
factor
);
trackInstantaneousMetric
(
STATS_METRIC_EL_CYCLE
,
server
.
duration_stats
[
EL_DURATION_TYPE_EL
].
cnt
,
current_time
,
factor
);
trackInstantaneousMetric
(
STATS_METRIC_EL_DURATION
,
server
.
duration_stats
[
EL_DURATION_TYPE_EL
].
sum
,
server
.
duration_stats
[
EL_DURATION_TYPE_EL
].
cnt
,
1
);
}
}
/* We have just LRU_BITS bits per object for LRU information.
/* We have just LRU_BITS bits per object for LRU information.
...
@@ -1537,6 +1546,9 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
...
@@ -1537,6 +1546,9 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
&
ei
);
&
ei
);
server
.
cronloops
++
;
server
.
cronloops
++
;
server
.
el_cron_duration
=
getMonotonicUs
()
-
cron_start
;
return
1000
/
server
.
hz
;
return
1000
/
server
.
hz
;
}
}
...
@@ -1672,6 +1684,11 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
...
@@ -1672,6 +1684,11 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
/* If any connection type(typical TLS) still has pending unread data don't sleep at all. */
/* If any connection type(typical TLS) still has pending unread data don't sleep at all. */
aeSetDontWait
(
server
.
el
,
connTypeHasPendingData
());
aeSetDontWait
(
server
.
el
,
connTypeHasPendingData
());
/* Record cron time in beforeSleep, which is the sum of active-expire, active-defrag and all other
* tasks done by cron and beforeSleep, but excluding read, write and AOF, that are counted by other
* sets of metrics. */
monotime
cron_start_time_before_aof
=
getMonotonicUs
();
/* Call the Redis Cluster before sleep function. Note that this function
/* Call the Redis Cluster before sleep function. Note that this function
* may change the state of Redis Cluster (from ok to fail or vice versa),
* may change the state of Redis Cluster (from ok to fail or vice versa),
* so it's a good idea to call it before serving the unblocked clients
* so it's a good idea to call it before serving the unblocked clients
...
@@ -1738,12 +1755,20 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
...
@@ -1738,12 +1755,20 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
* since the unblocked clients may write data. */
* since the unblocked clients may write data. */
handleClientsBlockedOnKeys
();
handleClientsBlockedOnKeys
();
/* Record time consumption of AOF writing. */
monotime
aof_start_time
=
getMonotonicUs
();
/* Record cron time in beforeSleep. This does not include the time consumed by AOF writing and IO writing below. */
monotime
duration_before_aof
=
aof_start_time
-
cron_start_time_before_aof
;
/* Write the AOF buffer on disk,
/* Write the AOF buffer on disk,
* must be done before handleClientsWithPendingWritesUsingThreads,
* must be done before handleClientsWithPendingWritesUsingThreads,
* in case of appendfsync=always. */
* in case of appendfsync=always. */
if
(
server
.
aof_state
==
AOF_ON
||
server
.
aof_state
==
AOF_WAIT_REWRITE
)
if
(
server
.
aof_state
==
AOF_ON
||
server
.
aof_state
==
AOF_WAIT_REWRITE
)
flushAppendOnlyFile
(
0
);
flushAppendOnlyFile
(
0
);
/* Record time consumption of AOF writing. */
durationAddSample
(
EL_DURATION_TYPE_AOF
,
getMonotonicUs
()
-
aof_start_time
);
/* Update the fsynced replica offset.
/* Update the fsynced replica offset.
* If an initial rewrite is in progress then not all data is guaranteed to have actually been
* If an initial rewrite is in progress then not all data is guaranteed to have actually been
* persisted to disk yet, so we cannot update the field. We will wait for the rewrite to complete. */
* persisted to disk yet, so we cannot update the field. We will wait for the rewrite to complete. */
...
@@ -1756,6 +1781,9 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
...
@@ -1756,6 +1781,9 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
/* Handle writes with pending output buffers. */
/* Handle writes with pending output buffers. */
handleClientsWithPendingWritesUsingThreads
();
handleClientsWithPendingWritesUsingThreads
();
/* Record cron time in beforeSleep. This does not include the time consumed by AOF writing and IO writing above. */
monotime
cron_start_time_after_write
=
getMonotonicUs
();
/* Close clients that need to be closed asynchronous */
/* Close clients that need to be closed asynchronous */
freeClientsInAsyncFreeQueue
();
freeClientsInAsyncFreeQueue
();
...
@@ -1767,6 +1795,25 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
...
@@ -1767,6 +1795,25 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
/* Disconnect some clients if they are consuming too much memory. */
/* Disconnect some clients if they are consuming too much memory. */
evictClients
();
evictClients
();
/* Record cron time in beforeSleep. */
monotime
duration_after_write
=
getMonotonicUs
()
-
cron_start_time_after_write
;
/* Record eventloop latency. */
if
(
server
.
el_start
>
0
)
{
monotime
el_duration
=
getMonotonicUs
()
-
server
.
el_start
;
durationAddSample
(
EL_DURATION_TYPE_EL
,
el_duration
);
}
server
.
el_cron_duration
+=
duration_before_aof
+
duration_after_write
;
durationAddSample
(
EL_DURATION_TYPE_CRON
,
server
.
el_cron_duration
);
server
.
el_cron_duration
=
0
;
/* Record max command count per cycle. */
if
(
server
.
stat_numcommands
>
server
.
el_cmd_cnt_start
)
{
long
long
el_command_cnt
=
server
.
stat_numcommands
-
server
.
el_cmd_cnt_start
;
if
(
el_command_cnt
>
server
.
el_cmd_cnt_max
)
{
server
.
el_cmd_cnt_max
=
el_command_cnt
;
}
}
/* Before we are going to sleep, let the threads access the dataset by
/* Before we are going to sleep, let the threads access the dataset by
* releasing the GIL. Redis main thread will not touch anything at this
* releasing the GIL. Redis main thread will not touch anything at this
* time. */
* time. */
...
@@ -1797,6 +1844,10 @@ void afterSleep(struct aeEventLoop *eventLoop) {
...
@@ -1797,6 +1844,10 @@ void afterSleep(struct aeEventLoop *eventLoop) {
latencyEndMonitor
(
latency
);
latencyEndMonitor
(
latency
);
latencyAddSampleIfNeeded
(
"module-acquire-GIL"
,
latency
);
latencyAddSampleIfNeeded
(
"module-acquire-GIL"
,
latency
);
}
}
/* Set the eventloop start time. */
server
.
el_start
=
getMonotonicUs
();
/* Set the eventloop command count at start. */
server
.
el_cmd_cnt_start
=
server
.
stat_numcommands
;
}
}
/* Update the time cache. */
/* Update the time cache. */
...
@@ -2517,8 +2568,8 @@ void resetServerStats(void) {
...
@@ -2517,8 +2568,8 @@ void resetServerStats(void) {
atomicSet
(
server
.
stat_total_writes_processed
,
0
);
atomicSet
(
server
.
stat_total_writes_processed
,
0
);
for
(
j
=
0
;
j
<
STATS_METRIC_COUNT
;
j
++
)
{
for
(
j
=
0
;
j
<
STATS_METRIC_COUNT
;
j
++
)
{
server
.
inst_metric
[
j
].
idx
=
0
;
server
.
inst_metric
[
j
].
idx
=
0
;
server
.
inst_metric
[
j
].
last_sample_
tim
e
=
mstime
()
;
server
.
inst_metric
[
j
].
last_sample_
bas
e
=
0
;
server
.
inst_metric
[
j
].
last_sample_
count
=
0
;
server
.
inst_metric
[
j
].
last_sample_
value
=
0
;
memset
(
server
.
inst_metric
[
j
].
samples
,
0
,
memset
(
server
.
inst_metric
[
j
].
samples
,
0
,
sizeof
(
server
.
inst_metric
[
j
].
samples
));
sizeof
(
server
.
inst_metric
[
j
].
samples
));
}
}
...
@@ -2535,6 +2586,8 @@ void resetServerStats(void) {
...
@@ -2535,6 +2586,8 @@ void resetServerStats(void) {
server
.
aof_delayed_fsync
=
0
;
server
.
aof_delayed_fsync
=
0
;
server
.
stat_reply_buffer_shrinks
=
0
;
server
.
stat_reply_buffer_shrinks
=
0
;
server
.
stat_reply_buffer_expands
=
0
;
server
.
stat_reply_buffer_expands
=
0
;
memset
(
server
.
duration_stats
,
0
,
sizeof
(
durationStats
)
*
EL_DURATION_TYPE_NUM
);
server
.
el_cmd_cnt_max
=
0
;
lazyfreeResetStats
();
lazyfreeResetStats
();
}
}
...
@@ -3150,7 +3203,7 @@ struct redisCommand *lookupCommandBySdsLogic(dict *commands, sds s) {
...
@@ -3150,7 +3203,7 @@ struct redisCommand *lookupCommandBySdsLogic(dict *commands, sds s) {
sds
*
strings
=
sdssplitlen
(
s
,
sdslen
(
s
),
"|"
,
1
,
&
argc
);
sds
*
strings
=
sdssplitlen
(
s
,
sdslen
(
s
),
"|"
,
1
,
&
argc
);
if
(
strings
==
NULL
)
if
(
strings
==
NULL
)
return
NULL
;
return
NULL
;
if
(
argc
>
2
)
{
if
(
argc
<
1
||
argc
>
2
)
{
/* Currently we support just one level of subcommands */
/* Currently we support just one level of subcommands */
sdsfreesplitres
(
strings
,
argc
);
sdsfreesplitres
(
strings
,
argc
);
return
NULL
;
return
NULL
;
...
@@ -3559,6 +3612,8 @@ void call(client *c, int flags) {
...
@@ -3559,6 +3612,8 @@ void call(client *c, int flags) {
char
*
latency_event
=
(
real_cmd
->
flags
&
CMD_FAST
)
?
char
*
latency_event
=
(
real_cmd
->
flags
&
CMD_FAST
)
?
"fast-command"
:
"command"
;
"fast-command"
:
"command"
;
latencyAddSampleIfNeeded
(
latency_event
,
duration
/
1000
);
latencyAddSampleIfNeeded
(
latency_event
,
duration
/
1000
);
if
(
server
.
execution_nesting
==
0
)
durationAddSample
(
EL_DURATION_TYPE_CMD
,
duration
);
}
}
/* Log the command into the Slow log if needed.
/* Log the command into the Slow log if needed.
...
@@ -5915,7 +5970,12 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
...
@@ -5915,7 +5970,12 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
"io_threaded_reads_processed:%lld
\r\n
"
"io_threaded_reads_processed:%lld
\r\n
"
"io_threaded_writes_processed:%lld
\r\n
"
"io_threaded_writes_processed:%lld
\r\n
"
"reply_buffer_shrinks:%lld
\r\n
"
"reply_buffer_shrinks:%lld
\r\n
"
"reply_buffer_expands:%lld
\r\n
"
,
"reply_buffer_expands:%lld
\r\n
"
"eventloop_cycles:%llu
\r\n
"
"eventloop_duration_sum:%llu
\r\n
"
"eventloop_duration_cmd_sum:%llu
\r\n
"
"instantaneous_eventloop_cycles_per_sec:%llu
\r\n
"
"instantaneous_eventloop_duration_usec:%llu
\r\n
"
,
server
.
stat_numconnections
,
server
.
stat_numconnections
,
server
.
stat_numcommands
,
server
.
stat_numcommands
,
getInstantaneousMetric
(
STATS_METRIC_COMMAND
),
getInstantaneousMetric
(
STATS_METRIC_COMMAND
),
...
@@ -5965,7 +6025,12 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
...
@@ -5965,7 +6025,12 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
server
.
stat_io_reads_processed
,
server
.
stat_io_reads_processed
,
server
.
stat_io_writes_processed
,
server
.
stat_io_writes_processed
,
server
.
stat_reply_buffer_shrinks
,
server
.
stat_reply_buffer_shrinks
,
server
.
stat_reply_buffer_expands
);
server
.
stat_reply_buffer_expands
,
server
.
duration_stats
[
EL_DURATION_TYPE_EL
].
cnt
,
server
.
duration_stats
[
EL_DURATION_TYPE_EL
].
sum
,
server
.
duration_stats
[
EL_DURATION_TYPE_CMD
].
sum
,
getInstantaneousMetric
(
STATS_METRIC_EL_CYCLE
),
getInstantaneousMetric
(
STATS_METRIC_EL_DURATION
));
info
=
genRedisInfoStringACLStats
(
info
);
info
=
genRedisInfoStringACLStats
(
info
);
}
}
...
@@ -6215,6 +6280,21 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
...
@@ -6215,6 +6280,21 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
0
,
/* not a crash report */
0
,
/* not a crash report */
sections
);
sections
);
}
}
if
(
dictFind
(
section_dict
,
"debug"
)
!=
NULL
)
{
if
(
sections
++
)
info
=
sdscat
(
info
,
"
\r\n
"
);
info
=
sdscatprintf
(
info
,
"# Debug
\r\n
"
"eventloop_duration_aof_sum:%llu
\r\n
"
"eventloop_duration_cron_sum:%llu
\r\n
"
"eventloop_duration_max:%llu
\r\n
"
"eventloop_cmd_per_cycle_max:%lld
\r\n
"
,
server
.
duration_stats
[
EL_DURATION_TYPE_AOF
].
sum
,
server
.
duration_stats
[
EL_DURATION_TYPE_CRON
].
sum
,
server
.
duration_stats
[
EL_DURATION_TYPE_EL
].
max
,
server
.
el_cmd_cnt_max
);
}
return
info
;
return
info
;
}
}
...
...
src/server.h
View file @
0cdafe3c
...
@@ -170,7 +170,9 @@ struct hdr_histogram;
...
@@ -170,7 +170,9 @@ struct hdr_histogram;
#define STATS_METRIC_NET_OUTPUT 2
/* Bytes written to network. */
#define STATS_METRIC_NET_OUTPUT 2
/* Bytes written to network. */
#define STATS_METRIC_NET_INPUT_REPLICATION 3
/* Bytes read to network during replication. */
#define STATS_METRIC_NET_INPUT_REPLICATION 3
/* Bytes read to network during replication. */
#define STATS_METRIC_NET_OUTPUT_REPLICATION 4
/* Bytes written to network during replication. */
#define STATS_METRIC_NET_OUTPUT_REPLICATION 4
/* Bytes written to network during replication. */
#define STATS_METRIC_COUNT 5
#define STATS_METRIC_EL_CYCLE 5
/* Number of eventloop cycled. */
#define STATS_METRIC_EL_DURATION 6
/* Eventloop duration. */
#define STATS_METRIC_COUNT 7
/* Protocol and I/O related defines */
/* Protocol and I/O related defines */
#define PROTO_IOBUF_LEN (1024*16)
/* Generic I/O buffer size */
#define PROTO_IOBUF_LEN (1024*16)
/* Generic I/O buffer size */
...
@@ -1697,13 +1699,22 @@ struct redisServer {
...
@@ -1697,13 +1699,22 @@ struct redisServer {
/* The following two are used to track instantaneous metrics, like
/* The following two are used to track instantaneous metrics, like
* number of operations per second, network traffic. */
* number of operations per second, network traffic. */
struct
{
struct
{
long
long
last_sample_
tim
e
;
/* T
imestamp
of last sample in
ms
*/
long
long
last_sample_
bas
e
;
/* T
he divisor
of last sample
w
in
dow
*/
long
long
last_sample_
count
;
/* Count in
last sample */
long
long
last_sample_
value
;
/* The dividend of
last sample
window
*/
long
long
samples
[
STATS_METRIC_SAMPLES
];
long
long
samples
[
STATS_METRIC_SAMPLES
];
int
idx
;
int
idx
;
}
inst_metric
[
STATS_METRIC_COUNT
];
}
inst_metric
[
STATS_METRIC_COUNT
];
long
long
stat_reply_buffer_shrinks
;
/* Total number of output buffer shrinks */
long
long
stat_reply_buffer_shrinks
;
/* Total number of output buffer shrinks */
long
long
stat_reply_buffer_expands
;
/* Total number of output buffer expands */
long
long
stat_reply_buffer_expands
;
/* Total number of output buffer expands */
monotime
el_start
;
/* The following two are used to record the max number of commands executed in one eventloop.
* Note that commands in transactions are also counted. */
long
long
el_cmd_cnt_start
;
long
long
el_cmd_cnt_max
;
/* The sum of active-expire, active-defrag and all other tasks done by cron and beforeSleep,
but excluding read, write and AOF, which are counted by other sets of metrics. */
monotime
el_cron_duration
;
durationStats
duration_stats
[
EL_DURATION_TYPE_NUM
];
/* Configuration */
/* Configuration */
int
verbosity
;
/* Loglevel in redis.conf */
int
verbosity
;
/* Loglevel in redis.conf */
...
...
src/t_list.c
View file @
0cdafe3c
...
@@ -971,7 +971,7 @@ void lposCommand(client *c) {
...
@@ -971,7 +971,7 @@ void lposCommand(client *c) {
if
(
!
strcasecmp
(
opt
,
"RANK"
)
&&
moreargs
)
{
if
(
!
strcasecmp
(
opt
,
"RANK"
)
&&
moreargs
)
{
j
++
;
j
++
;
if
(
getLongFromObjectOrReply
(
c
,
c
->
argv
[
j
],
&
rank
,
NULL
)
!=
C_OK
)
if
(
get
Range
LongFromObjectOrReply
(
c
,
c
->
argv
[
j
],
-
LONG_MAX
,
LONG_MAX
,
&
rank
,
NULL
)
!=
C_OK
)
return
;
return
;
if
(
rank
==
0
)
{
if
(
rank
==
0
)
{
addReplyError
(
c
,
"RANK can't be zero: use 1 to start from "
addReplyError
(
c
,
"RANK can't be zero: use 1 to start from "
...
...
tests/unit/info.tcl
View file @
0cdafe3c
...
@@ -274,5 +274,68 @@ start_server {tags {"info" "external:skip"}} {
...
@@ -274,5 +274,68 @@ start_server {tags {"info" "external:skip"}} {
$rd close
$rd close
}
}
test
{
stats: eventloop metrics
}
{
set info1
[
r info stats
]
set cycle1
[
getInfoProperty $info1 eventloop_cycles
]
set el_sum1
[
getInfoProperty $info1 eventloop_duration_sum
]
set cmd_sum1
[
getInfoProperty $info1 eventloop_duration_cmd_sum
]
assert_morethan $cycle1 0
assert_morethan $el_sum1 0
assert_morethan $cmd_sum1 0
after 110
;
# default hz is 10, wait for a cron tick.
set info2
[
r info stats
]
set cycle2
[
getInfoProperty $info2 eventloop_cycles
]
set el_sum2
[
getInfoProperty $info2 eventloop_duration_sum
]
set cmd_sum2
[
getInfoProperty $info2 eventloop_duration_cmd_sum
]
assert_morethan $cycle2 $cycle1
assert_lessthan $cycle2
[
expr $cycle1+10
]
;
# we expect 2 or 3 cycles here, but allow some tolerance
assert_morethan $el_sum2 $el_sum1
assert_lessthan $el_sum2
[
expr $el_sum1+5000
]
;
# we expect roughly 100ms here, but allow some tolerance
assert_morethan $cmd_sum2 $cmd_sum1
assert_lessthan $cmd_sum2
[
expr $cmd_sum1+3000
]
;
# we expect about tens of ms here, but allow some tolerance
}
test
{
stats: instantaneous metrics
}
{
r config resetstat
after 1600
;
# hz is 10, wait for 16 cron tick so that sample array is fulfilled
set value
[
s instantaneous_eventloop_cycles_per_sec
]
assert_morethan $value 0
assert_lessthan $value 15
;
# default hz is 10
set value
[
s instantaneous_eventloop_duration_usec
]
assert_morethan $value 0
assert_lessthan $value 22000
;
# default hz is 10, so duration < 1000 / 10, allow some tolerance
}
test
{
stats: debug metrics
}
{
# make sure debug info is hidden
set info
[
r info
]
assert_equal
[
getInfoProperty $info eventloop_duration_aof_sum
]
{}
set info_all
[
r info all
]
assert_equal
[
getInfoProperty $info_all eventloop_duration_aof_sum
]
{}
set info1
[
r info debug
]
set aof1
[
getInfoProperty $info1 eventloop_duration_aof_sum
]
assert
{
$aof1
>= 0
}
set cron1
[
getInfoProperty $info1 eventloop_duration_cron_sum
]
assert
{
$cron1
> 0
}
set cycle_max1
[
getInfoProperty $info1 eventloop_cmd_per_cycle_max
]
assert
{
$cycle
_max1 > 0
}
set duration_max1
[
getInfoProperty $info1 eventloop_duration_max
]
assert
{
$duration
_max1 > 0
}
after 110
;
# hz is 10, wait for a cron tick.
set info2
[
r info debug
]
set aof2
[
getInfoProperty $info2 eventloop_duration_aof_sum
]
assert
{
$aof2
>= $aof1
}
;
# AOF is disabled, we expect $aof2 == $aof1, but allow some tolerance.
set cron2
[
getInfoProperty $info2 eventloop_duration_cron_sum
]
assert_morethan $cron2 $cron1
set cycle_max2
[
getInfoProperty $info2 eventloop_cmd_per_cycle_max
]
assert
{
$cycle
_max2 >= $cycle_max1
}
set duration_max2
[
getInfoProperty $info2 eventloop_duration_max
]
assert
{
$duration
_max2 >= $duration_max1
}
}
}
}
}
}
tests/unit/type/list.tcl
View file @
0cdafe3c
...
@@ -464,6 +464,7 @@ foreach {type large} [array get largevalue] {
...
@@ -464,6 +464,7 @@ foreach {type large} [array get largevalue] {
assert
{[
r LPOS mylist c RANK -1
]
== 7
}
assert
{[
r LPOS mylist c RANK -1
]
== 7
}
assert
{[
r LPOS mylist c RANK -2
]
== 6
}
assert
{[
r LPOS mylist c RANK -2
]
== 6
}
assert_error
"*RANK can't be zero: use 1 to start from the first match, 2 from the second ... or use negative to start*"
{
r LPOS mylist c RANK 0
}
assert_error
"*RANK can't be zero: use 1 to start from the first match, 2 from the second ... or use negative to start*"
{
r LPOS mylist c RANK 0
}
assert_error
"*value is out of range*"
{
r LPOS mylist c RANK -9223372036854775808
}
}
}
test
{
LPOS COUNT option
}
{
test
{
LPOS COUNT option
}
{
...
...
tests/unit/type/set.tcl
View file @
0cdafe3c
...
@@ -53,14 +53,18 @@ start_server {
...
@@ -53,14 +53,18 @@ start_server {
assert_equal
{
16 17
}
[
lsort
[
r smembers myset
]]
assert_equal
{
16 17
}
[
lsort
[
r smembers myset
]]
}
}
test
{
SMISMEMBER against non set
}
{
test
{
SMISMEMBER
SMEMBERS SCARD
against non set
}
{
r lpush mylist foo
r lpush mylist foo
assert_error WRONGTYPE*
{
r smismember mylist bar
}
assert_error WRONGTYPE*
{
r smismember mylist bar
}
assert_error WRONGTYPE*
{
r smembers mylist
}
assert_error WRONGTYPE*
{
r scard mylist
}
}
}
test
{
SMISMEMBER non existing key
}
{
test
{
SMISMEMBER
SMEMBERS SCARD against
non existing key
}
{
assert_equal
{
0
}
[
r smismember myset1 foo
]
assert_equal
{
0
}
[
r smismember myset1 foo
]
assert_equal
{
0 0
}
[
r smismember myset1 foo bar
]
assert_equal
{
0 0
}
[
r smismember myset1 foo bar
]
assert_equal
{}
[
r smembers myset1
]
assert_equal
{
0
}
[
r scard myset1
]
}
}
test
{
SMISMEMBER requires one or more members
}
{
test
{
SMISMEMBER requires one or more members
}
{
...
...
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