Add HOTKEYS HELP subcommand and fix hotkeys INFO section (#14785)
Some checks are pending
CI / test-ubuntu-latest (push) Waiting to run
CI / test-sanitizer-address (push) Waiting to run
CI / build-debian-old (push) Waiting to run
CI / build-macos-latest (push) Waiting to run
CI / build-32bit (push) Waiting to run
CI / build-libc-malloc (push) Waiting to run
CI / build-centos-jemalloc (push) Waiting to run
CI / build-old-chain-jemalloc (push) Waiting to run
Codecov / code-coverage (push) Waiting to run
External Server Tests / test-external-standalone (push) Waiting to run
External Server Tests / test-external-cluster (push) Waiting to run
External Server Tests / test-external-nodebug (push) Waiting to run
Reply-schemas linter / reply-schemas-linter (push) Waiting to run
Spellcheck / Spellcheck (push) Waiting to run

Each command having subcommands needs a HELP subcommand which is
currently missing for HOTKEYS.
Also the newly added section "Hotkeys" for INFO was messing up modules
INFOs in some cases.
Fixed both issues in this PR.
This commit is contained in:
Mincho Paskalev 2026-02-23 11:04:14 +02:00 committed by GitHub
parent 832c723f9c
commit 6ec7b16cc1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 88 additions and 8 deletions

View file

@ -7332,6 +7332,23 @@ const char *HOTKEYS_GET_Tips[] = {
#define HOTKEYS_GET_Keyspecs NULL
#endif
/********** HOTKEYS HELP ********************/
#ifndef SKIP_CMD_HISTORY_TABLE
/* HOTKEYS HELP history */
#define HOTKEYS_HELP_History NULL
#endif
#ifndef SKIP_CMD_TIPS_TABLE
/* HOTKEYS HELP tips */
#define HOTKEYS_HELP_Tips NULL
#endif
#ifndef SKIP_CMD_KEY_SPECS_TABLE
/* HOTKEYS HELP key specs */
#define HOTKEYS_HELP_Keyspecs NULL
#endif
/********** HOTKEYS RESET ********************/
#ifndef SKIP_CMD_HISTORY_TABLE
@ -7414,6 +7431,7 @@ const char *HOTKEYS_STOP_Tips[] = {
/* HOTKEYS command table */
struct COMMAND_STRUCT HOTKEYS_Subcommands[] = {
{MAKE_CMD("get","Returns lists of top K hotkeys depending on metrics chosen in HOTKEYS START command.","O(K) where K is the number of hotkeys returned.","8.6.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,HOTKEYS_GET_History,0,HOTKEYS_GET_Tips,3,hotkeysCommand,2,CMD_ADMIN|CMD_NOSCRIPT,0,HOTKEYS_GET_Keyspecs,0,NULL,0)},
{MAKE_CMD("help","Return helpful text about HOTKEYS command parameters.","O(1)","8.6.1",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,HOTKEYS_HELP_History,0,HOTKEYS_HELP_Tips,0,hotkeysCommand,2,CMD_LOADING|CMD_STALE,0,HOTKEYS_HELP_Keyspecs,0,NULL,0)},
{MAKE_CMD("reset","Release the resources used for hotkey tracking.","O(1)","8.6.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,HOTKEYS_RESET_History,0,HOTKEYS_RESET_Tips,1,hotkeysCommand,2,CMD_ADMIN|CMD_NOSCRIPT,0,HOTKEYS_RESET_Keyspecs,0,NULL,0)},
{MAKE_CMD("start","Starts hotkeys tracking.","O(1)","8.6.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,HOTKEYS_START_History,0,HOTKEYS_START_Tips,1,hotkeysCommand,-2,CMD_ADMIN|CMD_NOSCRIPT,0,HOTKEYS_START_Keyspecs,0,NULL,5),.args=HOTKEYS_START_Args},
{MAKE_CMD("stop","Stops hotkeys tracking.","O(1)","8.6.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,HOTKEYS_STOP_History,0,HOTKEYS_STOP_Tips,1,hotkeysCommand,2,CMD_ADMIN|CMD_NOSCRIPT,0,HOTKEYS_STOP_Keyspecs,0,NULL,0)},

View file

@ -0,0 +1,22 @@
{
"HELP": {
"summary": "Return helpful text about HOTKEYS command parameters.",
"complexity": "O(1)",
"group": "server",
"since": "8.6.1",
"arity": 2,
"container": "HOTKEYS",
"function": "hotkeysCommand",
"command_flags": [
"LOADING",
"STALE"
],
"reply_schema": {
"type": "array",
"description": "Helpful text about subcommands.",
"items": {
"type": "string"
}
}
}
}

View file

@ -266,7 +266,33 @@ void hotkeysCommand(client *c) {
char *sub = c->argv[1]->ptr;
if (!strcasecmp(sub, "START")) {
if (!strcasecmp(sub, "HELP")) {
const char *help[] = {
"START <METRICS count [CPU] [NET]> [COUNT k] [DURATION duration] [SAMPLE ratio] [SLOTS count slot...]",
" Starts hotkeys tracking with specified metrics.",
" * METRICS count [CPU] [NET]",
" Specify count of metrics and choose amongst:",
" - CPU: Track hotkeys by CPU time percentage",
" - NET: Track hotkeys by network bytes percentage",
" * COUNT k",
" Specifies the value of K for the top-K hotkeys tracking. Default: 10",
" * DURATION duration",
" Specifies tracking duration in seconds. 0 means tracking will continue until manually stopped. Default: 0",
" * SAMPLE ratio",
" Keys are tracked with probability 1/ratio. Default: 1 (tracks every key)",
" * SLOTS count slot...",
" Specify which slots to track keys from. Only available in cluster mode. Default: empty (track all slots)",
"STOP",
" Stop hotkeys tracking. Results are still available via GET",
"GET",
" Get results from hotkeys tracking.",
"RESET",
" Reset memory used for hotkeys tracking. Tracking must have been stopped.",
" Results will no longer be available after this command.",
NULL
};
addReplyHelp(c, help);
} else if (!strcasecmp(sub, "START")) {
/* HOTKEYS START
* <METRICS count [CPU] [NET]>
* [COUNT k]

View file

@ -6726,16 +6726,18 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
}
/* Hotkeys */
if (server.hotkeys &&
(all_sections || (dictFind(section_dict,"hotkeys") != NULL)))
if (all_sections || (dictFind(section_dict,"hotkeys") != NULL))
{
if (sections++) info = sdscat(info,"\r\n");
info = sdscatprintf(info, "# Hotkeys\r\n"
"hotkeys-tracking-active:%d\r\n"
"hotkeys-cmd-cpu-time:%lld\r\n",
server.hotkeys->active ? 1 : 0,
server.hotkeys->cpu_time);
info = sdscatprintf(info, "# Hotkeys\r\n");
if (server.hotkeys) {
info = sdscatprintf(info,
"hotkeys-tracking-active:%d\r\n"
"hotkeys-cmd-cpu-time:%lld\r\n",
server.hotkeys->active ? 1 : 0,
server.hotkeys->cpu_time);
}
}
/* Modules */

View file

@ -3,9 +3,11 @@
#include <string.h>
void InfoFunc(RedisModuleInfoCtx *ctx, int for_crash_report) {
static int info_func_calls = 0;
RedisModule_InfoAddSection(ctx, "");
RedisModule_InfoAddFieldLongLong(ctx, "global", -2);
RedisModule_InfoAddFieldULongLong(ctx, "uglobal", (unsigned long long)-2);
RedisModule_InfoAddFieldLongLong(ctx, "info_calls", ++info_func_calls);
RedisModule_InfoAddSection(ctx, "Spanish");
RedisModule_InfoAddFieldCString(ctx, "uno", "one");

View file

@ -10,6 +10,16 @@ proc field {info property} {
start_server {tags {"modules external:skip"}} {
r module load $testmodule log-key 0
test {module info not attempted in INFO ALL} {
# call INFO in a few different ways, check that regardless of the section filtering,
# the module isn't at all being called when unneeded.
r INFO
r INFO all
r INFO memory
set info [r info everything]
set calls [field $info infotest_info_calls]
} {1}
test {module reading info} {
# check string, integer and float fields
assert_equal [r info.gets replication role] "master"