Add CLIENT NO-TOUCH for clients to run commands without affecting LRU/LFU of keys (#11483)
When no-touch mode is enabled, the client will not touch LRU/LFU of the keys it accesses, except when executing command `TOUCH`. This allows inspecting or modifying the key-space without affecting their eviction. Changes: - A command `CLIENT NO-TOUCH ON|OFF` to switch on and off this mode. - A client flag `#define CLIENT_NOTOUCH (1ULL<<45)`, which can be shown with `CLIENT INFO`, by the letter "T" in the "flags" field. - Clear `NO-TOUCH` flag in `clearClientConnectionState`, which is used by `RESET` command and resetting temp clients used by modules. - Also clear `NO-EVICT` flag in `clearClientConnectionState`, this might have been an oversight, spotted by @madolson. - A test using `DEBUG OBJECT` command to verify that LRU stat is not touched when no-touch mode is on. Co-authored-by: chentianjie <chentianjie@alibaba-inc.com> Co-authored-by: Madelyn Olson <34459052+madolson@users.noreply.github.com> Co-authored-by: sundb <sundbcn@gmail.com>
This commit is contained in:
parent
cd58af4d7f
commit
897c3d522c
@ -890,6 +890,27 @@ struct redisCommandArg CLIENT_NO_EVICT_Args[] = {
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** CLIENT NO_TOUCH ********************/
|
||||
|
||||
/* CLIENT NO_TOUCH history */
|
||||
#define CLIENT_NO_TOUCH_History NULL
|
||||
|
||||
/* CLIENT NO_TOUCH tips */
|
||||
#define CLIENT_NO_TOUCH_tips NULL
|
||||
|
||||
/* CLIENT NO_TOUCH enabled argument table */
|
||||
struct redisCommandArg CLIENT_NO_TOUCH_enabled_Subargs[] = {
|
||||
{"on",ARG_TYPE_PURE_TOKEN,-1,"ON",NULL,NULL,CMD_ARG_NONE},
|
||||
{"off",ARG_TYPE_PURE_TOKEN,-1,"OFF",NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/* CLIENT NO_TOUCH argument table */
|
||||
struct redisCommandArg CLIENT_NO_TOUCH_Args[] = {
|
||||
{"enabled",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=CLIENT_NO_TOUCH_enabled_Subargs},
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** CLIENT PAUSE ********************/
|
||||
|
||||
/* CLIENT PAUSE history */
|
||||
@ -1027,6 +1048,7 @@ struct redisCommand CLIENT_Subcommands[] = {
|
||||
{"kill","Kill the connection of a client","O(N) where N is the number of client connections","2.4.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_KILL_History,CLIENT_KILL_tips,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_KILL_Args},
|
||||
{"list","Get the list of client connections","O(N) where N is the number of client connections","2.4.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_LIST_History,CLIENT_LIST_tips,clientCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_LIST_Args},
|
||||
{"no-evict","Set client eviction mode for the current connection","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_NO_EVICT_History,CLIENT_NO_EVICT_tips,clientCommand,3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_NO_EVICT_Args},
|
||||
{"no-touch","Controls whether commands sent by the client will alter the LRU/LFU of the keys they access.","O(1)",NULL,CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_NO_TOUCH_History,CLIENT_NO_TOUCH_tips,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,.args=CLIENT_NO_TOUCH_Args},
|
||||
{"pause","Stop processing commands from clients for some time","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_PAUSE_History,CLIENT_PAUSE_tips,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_PAUSE_Args},
|
||||
{"reply","Instruct the server whether to reply to commands","O(1)","3.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_REPLY_History,CLIENT_REPLY_tips,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_REPLY_Args},
|
||||
{"setname","Set the current connection name","O(1)","2.6.9",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_SETNAME_History,CLIENT_SETNAME_tips,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_SETNAME_Args},
|
||||
|
36
src/commands/client-no-touch.json
Normal file
36
src/commands/client-no-touch.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"NO-TOUCH": {
|
||||
"summary": "Controls whether commands sent by the client will alter the LRU/LFU of the keys they access.",
|
||||
"complexity": "O(1)",
|
||||
"group": "connection",
|
||||
"arity": 3,
|
||||
"container": "CLIENT",
|
||||
"function": "clientCommand",
|
||||
"command_flags": [
|
||||
"NOSCRIPT",
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"acl_categories": [
|
||||
"CONNECTION"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "enabled",
|
||||
"type": "oneof",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "on",
|
||||
"type": "pure-token",
|
||||
"token": "ON"
|
||||
},
|
||||
{
|
||||
"name": "off",
|
||||
"type": "pure-token",
|
||||
"token": "OFF"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
3
src/db.c
3
src/db.c
@ -113,6 +113,9 @@ robj *lookupKey(redisDb *db, robj *key, int flags) {
|
||||
/* Update the access time for the ageing algorithm.
|
||||
* Don't do it if we have a saving child, as this will trigger
|
||||
* a copy on write madness. */
|
||||
if (server.current_client && server.current_client->flags & CLIENT_NO_TOUCH &&
|
||||
server.current_client->cmd->proc != touchCommand)
|
||||
flags |= LOOKUP_NOTOUCH;
|
||||
if (!hasActiveChildProcess() && !(flags & LOOKUP_NOTOUCH)){
|
||||
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
|
||||
updateLFU(val);
|
||||
|
@ -1493,8 +1493,8 @@ void clearClientConnectionState(client *c) {
|
||||
}
|
||||
|
||||
/* Selectively clear state flags not covered above */
|
||||
c->flags &= ~(CLIENT_ASKING|CLIENT_READONLY|CLIENT_PUBSUB|
|
||||
CLIENT_REPLY_OFF|CLIENT_REPLY_SKIP_NEXT);
|
||||
c->flags &= ~(CLIENT_ASKING|CLIENT_READONLY|CLIENT_PUBSUB|CLIENT_REPLY_OFF|
|
||||
CLIENT_REPLY_SKIP_NEXT|CLIENT_NO_TOUCH|CLIENT_NO_EVICT);
|
||||
}
|
||||
|
||||
void freeClient(client *c) {
|
||||
@ -2692,7 +2692,7 @@ char *getClientSockname(client *c) {
|
||||
/* Concatenate a string representing the state of a client in a human
|
||||
* readable format, into the sds string 's'. */
|
||||
sds catClientInfoString(sds s, client *client) {
|
||||
char flags[16], events[3], conninfo[CONN_INFO_LEN], *p;
|
||||
char flags[17], events[3], conninfo[CONN_INFO_LEN], *p;
|
||||
|
||||
p = flags;
|
||||
if (client->flags & CLIENT_SLAVE) {
|
||||
@ -2715,6 +2715,7 @@ sds catClientInfoString(sds s, client *client) {
|
||||
if (client->flags & CLIENT_UNIX_SOCKET) *p++ = 'U';
|
||||
if (client->flags & CLIENT_READONLY) *p++ = 'r';
|
||||
if (client->flags & CLIENT_NO_EVICT) *p++ = 'e';
|
||||
if (client->flags & CLIENT_NO_TOUCH) *p++ = 'T';
|
||||
if (p == flags) *p++ = 'N';
|
||||
*p++ = '\0';
|
||||
|
||||
@ -2905,6 +2906,8 @@ void clientCommand(client *c) {
|
||||
" Report tracking status for the current connection.",
|
||||
"NO-EVICT (ON|OFF)",
|
||||
" Protect current client connection from eviction.",
|
||||
"NO-TOUCH (ON|OFF)",
|
||||
" Will not touch LRU/LFU stats when this mode is on.",
|
||||
NULL
|
||||
};
|
||||
addReplyHelp(c, help);
|
||||
@ -3374,6 +3377,17 @@ NULL
|
||||
} else {
|
||||
addReplyArrayLen(c,0);
|
||||
}
|
||||
} else if (!strcasecmp(c->argv[1]->ptr, "no-touch")) {
|
||||
/* CLIENT NO-TOUCH ON|OFF */
|
||||
if (!strcasecmp(c->argv[2]->ptr,"on")) {
|
||||
c->flags |= CLIENT_NO_TOUCH;
|
||||
addReply(c,shared.ok);
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"off")) {
|
||||
c->flags &= ~CLIENT_NO_TOUCH;
|
||||
addReply(c,shared.ok);
|
||||
} else {
|
||||
addReplyErrorObject(c,shared.syntaxerr);
|
||||
}
|
||||
} else {
|
||||
addReplySubcommandSyntaxError(c);
|
||||
}
|
||||
|
@ -390,6 +390,7 @@ extern int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT];
|
||||
memory eviction. */
|
||||
#define CLIENT_ALLOW_OOM (1ULL<<44) /* Client used by RM_Call is allowed to fully execute
|
||||
scripts even when in OOM */
|
||||
#define CLIENT_NO_TOUCH (1ULL<<45) /* This client will not touch LFU/LRU stats. */
|
||||
|
||||
/* Client block type (btype field in client structure)
|
||||
* if CLIENT_BLOCKED flag is set. */
|
||||
|
@ -2,6 +2,17 @@ proc cmdstat {cmd} {
|
||||
return [cmdrstat $cmd r]
|
||||
}
|
||||
|
||||
proc getlru {key} {
|
||||
set objinfo [r debug object $key]
|
||||
foreach info $objinfo {
|
||||
set kvinfo [split $info ":"]
|
||||
if {[string compare [lindex $kvinfo 0] "lru"] == 0} {
|
||||
return [lindex $kvinfo 1]
|
||||
}
|
||||
}
|
||||
fail "Can't get LRU info with DEBUG OBJECT"
|
||||
}
|
||||
|
||||
start_server {tags {"introspection"}} {
|
||||
test {The microsecond part of the TIME command will not overflow} {
|
||||
set now [r time]
|
||||
@ -26,6 +37,20 @@ start_server {tags {"introspection"}} {
|
||||
assert {[r object idletime foo] < 2}
|
||||
}
|
||||
|
||||
test {Operations in no-touch mode do not alter the last access time of a key} {
|
||||
r set foo bar
|
||||
r client no-touch on
|
||||
set oldlru [getlru foo]
|
||||
after 1000
|
||||
r get foo
|
||||
set newlru [getlru foo]
|
||||
assert_equal $newlru $oldlru
|
||||
r client no-touch off
|
||||
r get foo
|
||||
set newlru [getlru foo]
|
||||
assert_morethan $newlru $oldlru
|
||||
} {} {needs:debug}
|
||||
|
||||
test {TOUCH returns the number of existing keys specified} {
|
||||
r flushdb
|
||||
r set key1{t} 1
|
||||
|
Loading…
x
Reference in New Issue
Block a user