diff --git a/src/blocked.c b/src/blocked.c index ad815a9b6c..ec1d377020 100644 --- a/src/blocked.c +++ b/src/blocked.c @@ -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++; diff --git a/src/networking.c b/src/networking.c index 25b9f096ec..cd65e37273 100644 --- a/src/networking.c +++ b/src/networking.c @@ -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; } @@ -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) { @@ -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, @@ -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; } diff --git a/src/server.c b/src/server.c index e30338e452..370d8fb8eb 100644 --- a/src/server.c +++ b/src/server.c @@ -3716,8 +3716,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. */ diff --git a/src/server.h b/src/server.h index d891fccfb1..ca75dc8d5b 100644 --- a/src/server.h +++ b/src/server.h @@ -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 */ diff --git a/tests/unit/introspection.tcl b/tests/unit/introspection.tcl index b022431fe3..90d37aca44 100644 --- a/tests/unit/introspection.tcl +++ b/tests/unit/introspection.tcl @@ -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] @@ -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}