mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 08:08:53 -05:00
Implement CLIENT KILL MAXAGE <maxage>
(#12299)
Adds an ability to kill clients older than a specified age. Also, fixed the age calculation in `catClientInfoString` to use `commandTimeSnapshot` instead of the old `server.unixtime`, and added missing documentation for `CLIENT KILL ID` to output of `CLIENT help`. --------- Co-authored-by: Oran Agra <oran@redislabs.com>
This commit is contained in:
parent
7c9f41b52b
commit
24f6d08b3f
@ -1177,6 +1177,7 @@ commandHistory CLIENT_KILL_History[] = {
|
||||
{"3.2.0","Added `master` type in for `TYPE` option."},
|
||||
{"5.0.0","Replaced `slave` `TYPE` with `replica`. `slave` still supported for backward compatibility."},
|
||||
{"6.2.0","`LADDR` option."},
|
||||
{"8.0.0","`MAXAGE` option."},
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -1213,12 +1214,13 @@ struct COMMAND_ARG CLIENT_KILL_filter_new_format_Subargs[] = {
|
||||
{MAKE_ARG("addr",ARG_TYPE_STRING,-1,"ADDR",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL),.display_text="ip:port"},
|
||||
{MAKE_ARG("laddr",ARG_TYPE_STRING,-1,"LADDR",NULL,"6.2.0",CMD_ARG_OPTIONAL,0,NULL),.display_text="ip:port"},
|
||||
{MAKE_ARG("skipme",ARG_TYPE_ONEOF,-1,"SKIPME",NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=CLIENT_KILL_filter_new_format_skipme_Subargs},
|
||||
{MAKE_ARG("maxage",ARG_TYPE_INTEGER,-1,"MAXAGE",NULL,"8.0.0",CMD_ARG_OPTIONAL,0,NULL)},
|
||||
};
|
||||
|
||||
/* CLIENT KILL filter argument table */
|
||||
struct COMMAND_ARG CLIENT_KILL_filter_Subargs[] = {
|
||||
{MAKE_ARG("old-format",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,"2.8.12"),.display_text="ip:port"},
|
||||
{MAKE_ARG("new-format",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,6,NULL),.subargs=CLIENT_KILL_filter_new_format_Subargs},
|
||||
{MAKE_ARG("new-format",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,7,NULL),.subargs=CLIENT_KILL_filter_new_format_Subargs},
|
||||
};
|
||||
|
||||
/* CLIENT KILL argument table */
|
||||
@ -1543,7 +1545,7 @@ struct COMMAND_STRUCT CLIENT_Subcommands[] = {
|
||||
{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_HELP_History,0,CLIENT_HELP_Tips,0,clientCommand,2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_HELP_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("id","Returns the unique client ID of the connection.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_ID_History,0,CLIENT_ID_Tips,0,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_ID_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("info","Returns information about the connection.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_INFO_History,0,CLIENT_INFO_Tips,1,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_INFO_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("kill","Terminates open connections.","O(N) where N is the number of client connections","2.4.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_KILL_History,5,CLIENT_KILL_Tips,0,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_KILL_Keyspecs,0,NULL,1),.args=CLIENT_KILL_Args},
|
||||
{MAKE_CMD("kill","Terminates open connections.","O(N) where N is the number of client connections","2.4.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_KILL_History,6,CLIENT_KILL_Tips,0,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_KILL_Keyspecs,0,NULL,1),.args=CLIENT_KILL_Args},
|
||||
{MAKE_CMD("list","Lists open connections.","O(N) where N is the number of client connections","2.4.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_LIST_History,6,CLIENT_LIST_Tips,1,clientCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_LIST_Keyspecs,0,NULL,2),.args=CLIENT_LIST_Args},
|
||||
{MAKE_CMD("no-evict","Sets the client eviction mode of the connection.","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_NO_EVICT_History,0,CLIENT_NO_EVICT_Tips,0,clientCommand,3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_NO_EVICT_Keyspecs,0,NULL,1),.args=CLIENT_NO_EVICT_Args},
|
||||
{MAKE_CMD("no-touch","Controls whether commands sent by the client affect the LRU/LFU of accessed keys.","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_NO_TOUCH_History,0,CLIENT_NO_TOUCH_Tips,0,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,CLIENT_NO_TOUCH_Keyspecs,0,NULL,1),.args=CLIENT_NO_TOUCH_Args},
|
||||
|
@ -27,6 +27,10 @@
|
||||
[
|
||||
"6.2.0",
|
||||
"`LADDR` option."
|
||||
],
|
||||
[
|
||||
"8.0.0",
|
||||
"`MAXAGE` option."
|
||||
]
|
||||
],
|
||||
"command_flags": [
|
||||
@ -136,6 +140,13 @@
|
||||
"token": "NO"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"token": "MAXAGE",
|
||||
"name": "maxage",
|
||||
"type": "integer",
|
||||
"optional": true,
|
||||
"since": "8.0.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -2850,7 +2850,7 @@ sds catClientInfoString(sds s, client *client) {
|
||||
" laddr=%s", getClientSockname(client),
|
||||
" %s", connGetInfo(client->conn, conninfo, sizeof(conninfo)),
|
||||
" name=%s", client->name ? (char*)client->name->ptr : "",
|
||||
" age=%I", (long long)(server.unixtime - client->ctime),
|
||||
" age=%I", (long long)(commandTimeSnapshot() / 1000 - client->ctime),
|
||||
" idle=%I", (long long)(server.unixtime - client->lastinteraction),
|
||||
" flags=%s", flags,
|
||||
" db=%i", client->db->id,
|
||||
@ -3042,6 +3042,10 @@ void clientCommand(client *c) {
|
||||
" Kill connections authenticated by <username>.",
|
||||
" * SKIPME (YES|NO)",
|
||||
" Skip killing current connection (default: yes).",
|
||||
" * ID <client-id>",
|
||||
" Kill connections by client id.",
|
||||
" * MAXAGE <maxage>",
|
||||
" Kill connections older than the specified age.",
|
||||
"LIST [options ...]",
|
||||
" Return information about client connections. Options:",
|
||||
" * TYPE (NORMAL|MASTER|REPLICA|PUBSUB)",
|
||||
@ -3153,6 +3157,7 @@ NULL
|
||||
user *user = NULL;
|
||||
int type = -1;
|
||||
uint64_t id = 0;
|
||||
long long max_age = 0;
|
||||
int skipme = 1;
|
||||
int killed = 0, close_this_client = 0;
|
||||
|
||||
@ -3174,6 +3179,18 @@ NULL
|
||||
"client-id should be greater than 0") != C_OK)
|
||||
return;
|
||||
id = tmp;
|
||||
} else if (!strcasecmp(c->argv[i]->ptr,"maxage") && moreargs) {
|
||||
long long tmp;
|
||||
|
||||
if (getLongLongFromObjectOrReply(c, c->argv[i+1], &tmp,
|
||||
"maxage is not an integer or out of range") != C_OK)
|
||||
return;
|
||||
if (tmp <= 0) {
|
||||
addReplyError(c, "maxage should be greater than 0");
|
||||
return;
|
||||
}
|
||||
|
||||
max_age = tmp;
|
||||
} else if (!strcasecmp(c->argv[i]->ptr,"type") && moreargs) {
|
||||
type = getClientTypeByName(c->argv[i+1]->ptr);
|
||||
if (type == -1) {
|
||||
@ -3223,6 +3240,7 @@ NULL
|
||||
if (id != 0 && client->id != id) continue;
|
||||
if (user && client->user != user) continue;
|
||||
if (c == client && skipme) continue;
|
||||
if (max_age != 0 && (long long)(commandTimeSnapshot() / 1000 - client->ctime) < max_age) continue;
|
||||
|
||||
/* Kill it. */
|
||||
if (c == client) {
|
||||
|
@ -32,8 +32,35 @@ start_server {tags {"introspection"}} {
|
||||
assert_error "ERR No such user*" {r client kill user wrong_user}
|
||||
|
||||
assert_error "ERR syntax error*" {r client kill skipme yes_or_no}
|
||||
|
||||
assert_error "ERR *not an integer or out of range*" {r client kill maxage str}
|
||||
assert_error "ERR *not an integer or out of range*" {r client kill maxage 9999999999999999999}
|
||||
assert_error "ERR *greater than 0*" {r client kill maxage -1}
|
||||
}
|
||||
|
||||
test {CLIENT KILL maxAGE will kill old clients} {
|
||||
set rd1 [redis_deferring_client]
|
||||
r debug sleep 2
|
||||
set rd2 [redis_deferring_client]
|
||||
|
||||
r acl setuser dummy on nopass +ping
|
||||
$rd1 auth dummy ""
|
||||
$rd1 read
|
||||
$rd2 auth dummy ""
|
||||
$rd2 read
|
||||
|
||||
# Should kill rd1 but not rd2
|
||||
set res [r client kill user dummy maxage 1]
|
||||
assert {$res == 1}
|
||||
|
||||
# rd2 should still be connected
|
||||
$rd2 ping
|
||||
assert_equal "PONG" [$rd2 read]
|
||||
|
||||
$rd1 close
|
||||
$rd2 close
|
||||
} {0} {"needs:debug"}
|
||||
|
||||
test {CLIENT KILL SKIPME YES/NO will kill all clients} {
|
||||
# Kill all clients except `me`
|
||||
set rd1 [redis_deferring_client]
|
||||
|
Loading…
Reference in New Issue
Block a user