Skip to content

Commit

Permalink
Input output traffic stats and command process count for each client. (
Browse files Browse the repository at this point in the history
…#327)

We already have global stats for input traffic, output traffic and how
many commands have been executed.

However, some users have the difficulty of locating the IP(s) which have
heavy network traffic. So here some stats for single client are
introduced.
```              
tot-net-in   // Total network input bytes read from the client
tot-net-out  // Total network output bytes sent to the client
tot-cmds     // Total commands the client has executed     
```                             
These three stats are shown in `CLIENT LIST` and `CLIENT INFO`.

Though the metrics are handled in hot paths of the code, personally I
don't think it will slow down the server. Considering all other complex
operations handled nearby, this is only a small and simple operation.

However we do need to be cautious when adding more and more metrics, as
discussed in redis/redis#12640, we may need to find a way to tell
whether this has obvious performance degradation.

---------

Signed-off-by: Chen Tianjie <TJ_Chen@outlook.com>
  • Loading branch information
CharlesChen888 committed May 6, 2024
1 parent 9ebbd5f commit cc703aa
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/blocked.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ void updateStatsOnUnblock(client *c, long blocked_us, long reply_us, int had_err
const ustime_t total_cmd_duration = c->duration + blocked_us + reply_us;
c->lastcmd->microseconds += total_cmd_duration;
c->lastcmd->calls++;
c->commands_processed++;
server.stat_numcommands++;
if (had_errors)
c->lastcmd->failed_calls++;
Expand Down
10 changes: 9 additions & 1 deletion src/networking.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ client *createClient(connection *conn) {
c->mem_usage_bucket_node = NULL;
if (conn) linkClient(c);
initClientMultiState(c);
c->net_input_bytes = 0;
c->net_output_bytes = 0;
c->commands_processed = 0;
return c;
}

Expand Down Expand Up @@ -1991,6 +1994,7 @@ int writeToClient(client *c, int handler_installed) {
} else {
atomicIncr(server.stat_net_output_bytes, totwritten);
}
c->net_output_bytes += totwritten;

if (nwritten == -1) {
if (connGetState(c->conn) != CONN_STATE_CONNECTED) {
Expand Down Expand Up @@ -2718,6 +2722,7 @@ void readQueryFromClient(connection *conn) {
} else {
atomicIncr(server.stat_net_input_bytes, nread);
}
c->net_input_bytes += nread;

if (!(c->flags & CLIENT_MASTER) &&
/* The commands cached in the MULTI/EXEC queue have not been executed yet,
Expand Down Expand Up @@ -2874,7 +2879,10 @@ sds catClientInfoString(sds s, client *client) {
" redir=%I", (client->flags & CLIENT_TRACKING) ? (long long) client->client_tracking_redirection : -1,
" resp=%i", client->resp,
" lib-name=%s", client->lib_name ? (char*)client->lib_name->ptr : "",
" lib-ver=%s", client->lib_ver ? (char*)client->lib_ver->ptr : ""));
" lib-ver=%s", client->lib_ver ? (char*)client->lib_ver->ptr : "",
" tot-net-in=%U", client->net_input_bytes,
" tot-net-out=%U", client->net_output_bytes,
" tot-cmds=%U", client->commands_processed));
return ret;
}

Expand Down
8 changes: 7 additions & 1 deletion src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -3734,8 +3734,14 @@ void call(client *c, int flags) {
}
}

if (!(c->flags & CLIENT_BLOCKED))
if (!(c->flags & CLIENT_BLOCKED)) {
/* Modules may call commands in cron, in which case server.current_client
* is not set. */
if (server.current_client) {
server.current_client->commands_processed++;
}
server.stat_numcommands++;
}

/* Record peak memory after each command and before the eviction that runs
* before the next command. */
Expand Down
3 changes: 3 additions & 0 deletions src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -1282,6 +1282,9 @@ typedef struct client {
#ifdef LOG_REQ_RES
clientReqResInfo reqres;
#endif
unsigned long long net_input_bytes; /* Total network input bytes read from this client. */
unsigned long long net_output_bytes; /* Total network output bytes sent to this client. */
unsigned long long commands_processed; /* Total count of commands this client executed. */
} client;

/* ACL information */
Expand Down
73 changes: 71 additions & 2 deletions tests/unit/introspection.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ start_server {tags {"introspection"}} {

test {CLIENT LIST} {
r client list
} {id=* addr=*:* laddr=*:* fd=* name=* age=* idle=* flags=N db=* sub=0 psub=0 ssub=0 multi=-1 watch=0 qbuf=26 qbuf-free=* argv-mem=* multi-mem=0 rbs=* rbp=* obl=0 oll=0 omem=0 tot-mem=* events=r cmd=client|list user=* redir=-1 resp=* lib-name=* lib-ver=*}
} {id=* addr=*:* laddr=*:* fd=* name=* age=* idle=* flags=N db=* sub=0 psub=0 ssub=0 multi=-1 watch=0 qbuf=26 qbuf-free=* argv-mem=* multi-mem=0 rbs=* rbp=* obl=0 oll=0 omem=0 tot-mem=* events=r cmd=client|list user=* redir=-1 resp=* lib-name=* lib-ver=* tot-net-in=* tot-net-out=* tot-cmds=*}

test {CLIENT LIST with IDs} {
set myid [r client id]
Expand All @@ -17,7 +17,76 @@ start_server {tags {"introspection"}} {

test {CLIENT INFO} {
r client info
} {id=* addr=*:* laddr=*:* fd=* name=* age=* idle=* flags=N db=* sub=0 psub=0 ssub=0 multi=-1 watch=0 qbuf=26 qbuf-free=* argv-mem=* multi-mem=0 rbs=* rbp=* obl=0 oll=0 omem=0 tot-mem=* events=r cmd=client|info user=* redir=-1 resp=* lib-name=* lib-ver=*}
} {id=* addr=*:* laddr=*:* fd=* name=* age=* idle=* flags=N db=* sub=0 psub=0 ssub=0 multi=-1 watch=0 qbuf=26 qbuf-free=* argv-mem=* multi-mem=0 rbs=* rbp=* obl=0 oll=0 omem=0 tot-mem=* events=r cmd=client|info user=* redir=-1 resp=* lib-name=* lib-ver=* tot-net-in=* tot-net-out=* tot-cmds=*}

proc get_field_in_client_info {info field} {
set info [string trim $info]
foreach item [split $info " "] {
set kv [split $item "="]
set k [lindex $kv 0]
if {[string match $field $k]} {
return [lindex $kv 1]
}
}
return ""
}

proc get_field_in_client_list {id client_list filed} {
set list [split $client_list "\r\n"]
foreach info $list {
if {[string match "id=$id *" $info] } {
return [get_field_in_client_info $info $filed]
}
}
return ""
}

test {client input output and command process statistics} {
set info1 [r client info]
set input1 [get_field_in_client_info $info1 "tot-net-in"]
set output1 [get_field_in_client_info $info1 "tot-net-out"]
set cmd1 [get_field_in_client_info $info1 "tot-cmds"]
set info2 [r client info]
set input2 [get_field_in_client_info $info2 "tot-net-in"]
set output2 [get_field_in_client_info $info2 "tot-net-out"]
set cmd2 [get_field_in_client_info $info2 "tot-cmds"]
assert_equal [expr $input1+26] $input2
assert {[expr $output1+300] < $output2}
assert_equal [expr $cmd1+1] $cmd2
# test blocking command
r del mylist
set rd [valkey_deferring_client]
$rd client id
set rd_id [$rd read]
set info_list [r client list]
set input3 [get_field_in_client_list $rd_id $info_list "tot-net-in"]
set output3 [get_field_in_client_list $rd_id $info_list "tot-net-out"]
set cmd3 [get_field_in_client_list $rd_id $info_list "tot-cmds"]
$rd blpop mylist 0
set info_list [r client list]
set input4 [get_field_in_client_list $rd_id $info_list "tot-net-in"]
set output4 [get_field_in_client_list $rd_id $info_list "tot-net-out"]
set cmd4 [get_field_in_client_list $rd_id $info_list "tot-cmds"]
assert_equal [expr $input3+34] $input4
assert_equal $output3 $output4
assert_equal $cmd3 $cmd4
r lpush mylist a
set info_list [r client list]
set input5 [get_field_in_client_list $rd_id $info_list "tot-net-in"]
set output5 [get_field_in_client_list $rd_id $info_list "tot-net-out"]
set cmd5 [get_field_in_client_list $rd_id $info_list "tot-cmds"]
assert_equal $input4 $input5
assert_equal [expr $output4+23] $output5
assert_equal [expr $cmd4+1] $cmd5
$rd close
# test recursive command
set info [r client info]
set cmd6 [get_field_in_client_info $info "tot-cmds"]
r eval "server.call('ping')" 0
set info [r client info]
set cmd7 [get_field_in_client_info $info "tot-cmds"]
assert_equal [expr $cmd6+3] $cmd7
}

test {CLIENT KILL with illegal arguments} {
assert_error "ERR wrong number of arguments for 'client|kill' command" {r client kill}
Expand Down

0 comments on commit cc703aa

Please sign in to comment.