Skip to content

Commit 6b6f4f3

Browse files
committed
Added cache for COMMAND
Signed-off-by: Evgeny Barskiy <[email protected]>
1 parent 8e0b375 commit 6b6f4f3

File tree

3 files changed

+125
-21
lines changed

3 files changed

+125
-21
lines changed

src/module.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,6 +1385,8 @@ int VM_CreateCommand(ValkeyModuleCtx *ctx,
13851385
serverAssert(hashtableAdd(server.commands, cp->serverCmd));
13861386
serverAssert(hashtableAdd(server.orig_commands, cp->serverCmd));
13871387
cp->serverCmd->id = ACLGetCommandID(declared_name); /* ID used for ACL. */
1388+
/* Invalidate COMMAND response cache since we added a new command */
1389+
invalidateCommandCache();
13881390
return VALKEYMODULE_OK;
13891391
}
13901392

@@ -12530,6 +12532,14 @@ int moduleFreeCommand(struct ValkeyModule *module, struct serverCommand *cmd) {
1253012532
hdr_close(cmd->latency_histogram);
1253112533
cmd->latency_histogram = NULL;
1253212534
}
12535+
if (cmd->info_cache_resp2) {
12536+
sdsfree(cmd->info_cache_resp2);
12537+
cmd->info_cache_resp2 = NULL;
12538+
}
12539+
if (cmd->info_cache_resp3) {
12540+
sdsfree(cmd->info_cache_resp3);
12541+
cmd->info_cache_resp3 = NULL;
12542+
}
1253312543
moduleFreeArgs(cmd->args, cmd->num_args);
1253412544
zfree(cp);
1253512545

@@ -12571,6 +12581,8 @@ void moduleUnregisterCommands(struct ValkeyModule *module) {
1257112581
zfree(cmd);
1257212582
}
1257312583
hashtableResetIterator(&iter);
12584+
/* Invalidate COMMAND response cache since we removed commands */
12585+
invalidateCommandCache();
1257412586
}
1257512587

1257612588
/* We parse argv to add sds "NAME VALUE" pairs to the server.module_configs_queue list of configs.

src/server.c

Lines changed: 108 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2342,6 +2342,8 @@ void initServerConfig(void) {
23422342
* valkey.conf using the rename-command directive. */
23432343
server.commands = hashtableCreate(&commandSetType);
23442344
server.orig_commands = hashtableCreate(&originalCommandSetType);
2345+
server.command_response_cache_resp2 = NULL;
2346+
server.command_response_cache_resp3 = NULL;
23452347
populateCommandTable();
23462348

23472349
/* Debugging */
@@ -3282,6 +3284,10 @@ int populateCommandStructure(struct serverCommand *c) {
32823284
* has been issued for the first time */
32833285
c->latency_histogram = NULL;
32843286

3287+
/* Initialize command info cache */
3288+
c->info_cache_resp2 = NULL;
3289+
c->info_cache_resp3 = NULL;
3290+
32853291
/* Handle the legacy range spec and the "movablekeys" flag (must be done after populating all key specs). */
32863292
populateCommandLegacyRangeSpec(c);
32873293

@@ -5222,30 +5228,77 @@ void addReplyCommandSubCommands(client *c,
52225228
hashtableResetIterator(&iter);
52235229
}
52245230

5231+
/* Forward declaration */
5232+
void addReplyCommandInfo(client *c, struct serverCommand *cmd);
5233+
5234+
/* Collect all output from a caching client (both buffer and reply list) */
5235+
static sds collectCachedResponse(client *c) {
5236+
sds response = sdsempty();
5237+
5238+
/* First, collect from the fixed buffer if any */
5239+
if (c->bufpos > 0) {
5240+
response = sdscatlen(response, c->buf, c->bufpos);
5241+
}
5242+
5243+
/* Then, collect from the reply list */
5244+
listIter li;
5245+
listNode *ln;
5246+
clientReplyBlock *val_block;
5247+
listRewind(c->reply, &li);
5248+
while ((ln = listNext(&li)) != NULL) {
5249+
val_block = (clientReplyBlock *)listNodeValue(ln);
5250+
response = sdscatlen(response, val_block->buf, val_block->used);
5251+
}
5252+
5253+
return response;
5254+
}
5255+
5256+
/* Generate and cache the command info response for a given protocol version */
5257+
static void cacheCommandInfo(struct serverCommand *cmd, int resp) {
5258+
client *caching_client = createCachedResponseClient(resp);
5259+
5260+
int firstkey = 0, lastkey = 0, keystep = 0;
5261+
if (cmd->legacy_range_key_spec.begin_search_type != KSPEC_BS_INVALID) {
5262+
firstkey = cmd->legacy_range_key_spec.bs.index.pos;
5263+
lastkey = cmd->legacy_range_key_spec.fk.range.lastkey;
5264+
if (lastkey >= 0) lastkey += firstkey;
5265+
keystep = cmd->legacy_range_key_spec.fk.range.keystep;
5266+
}
5267+
5268+
addReplyArrayLen(caching_client, 10);
5269+
addReplyBulkCBuffer(caching_client, cmd->fullname, sdslen(cmd->fullname));
5270+
addReplyLongLong(caching_client, cmd->arity);
5271+
addReplyFlagsForCommand(caching_client, cmd);
5272+
addReplyLongLong(caching_client, firstkey);
5273+
addReplyLongLong(caching_client, lastkey);
5274+
addReplyLongLong(caching_client, keystep);
5275+
addReplyCommandCategories(caching_client, cmd);
5276+
addReplyCommandTips(caching_client, cmd);
5277+
addReplyCommandKeySpecs(caching_client, cmd);
5278+
addReplyCommandSubCommands(caching_client, cmd, addReplyCommandInfo, 0);
5279+
5280+
if (resp == 2) {
5281+
cmd->info_cache_resp2 = collectCachedResponse(caching_client);
5282+
} else {
5283+
cmd->info_cache_resp3 = collectCachedResponse(caching_client);
5284+
}
5285+
5286+
deleteCachedResponseClient(caching_client);
5287+
}
5288+
52255289
/* Output the representation of a server command. Used by the COMMAND command and COMMAND INFO. */
52265290
void addReplyCommandInfo(client *c, struct serverCommand *cmd) {
52275291
if (!cmd) {
52285292
addReplyNull(c);
52295293
} else {
5230-
int firstkey = 0, lastkey = 0, keystep = 0;
5231-
if (cmd->legacy_range_key_spec.begin_search_type != KSPEC_BS_INVALID) {
5232-
firstkey = cmd->legacy_range_key_spec.bs.index.pos;
5233-
lastkey = cmd->legacy_range_key_spec.fk.range.lastkey;
5234-
if (lastkey >= 0) lastkey += firstkey;
5235-
keystep = cmd->legacy_range_key_spec.fk.range.keystep;
5294+
/* Use cached response if available for the client's protocol version */
5295+
sds *cache = (c->resp == 2) ? &cmd->info_cache_resp2 : &cmd->info_cache_resp3;
5296+
5297+
if (*cache == NULL) {
5298+
cacheCommandInfo(cmd, c->resp);
52365299
}
5237-
5238-
addReplyArrayLen(c, 10);
5239-
addReplyBulkCBuffer(c, cmd->fullname, sdslen(cmd->fullname));
5240-
addReplyLongLong(c, cmd->arity);
5241-
addReplyFlagsForCommand(c, cmd);
5242-
addReplyLongLong(c, firstkey);
5243-
addReplyLongLong(c, lastkey);
5244-
addReplyLongLong(c, keystep);
5245-
addReplyCommandCategories(c, cmd);
5246-
addReplyCommandTips(c, cmd);
5247-
addReplyCommandKeySpecs(c, cmd);
5248-
addReplyCommandSubCommands(c, cmd, addReplyCommandInfo, 0);
5300+
5301+
addReplyProto(c, *cache, sdslen(*cache));
52495302
}
52505303
}
52515304

@@ -5371,16 +5424,50 @@ void getKeysSubcommand(client *c) {
53715424
}
53725425

53735426
/* COMMAND (no args) */
5374-
void commandCommand(client *c) {
5427+
/* Invalidate the cached COMMAND response when command table changes */
5428+
void invalidateCommandCache(void) {
5429+
if (server.command_response_cache_resp2) {
5430+
sdsfree(server.command_response_cache_resp2);
5431+
server.command_response_cache_resp2 = NULL;
5432+
}
5433+
if (server.command_response_cache_resp3) {
5434+
sdsfree(server.command_response_cache_resp3);
5435+
server.command_response_cache_resp3 = NULL;
5436+
}
5437+
}
5438+
5439+
/* Generate and cache the full COMMAND response */
5440+
static void cacheCommandResponse(int resp) {
5441+
client *caching_client = createCachedResponseClient(resp);
5442+
53755443
hashtableIterator iter;
53765444
void *next;
5377-
addReplyArrayLen(c, hashtableSize(server.commands));
5445+
addReplyArrayLen(caching_client, hashtableSize(server.commands));
53785446
hashtableInitIterator(&iter, server.commands, 0);
53795447
while (hashtableNext(&iter, &next)) {
53805448
struct serverCommand *cmd = next;
5381-
addReplyCommandInfo(c, cmd);
5449+
addReplyCommandInfo(caching_client, cmd);
53825450
}
53835451
hashtableResetIterator(&iter);
5452+
5453+
if (resp == 2) {
5454+
server.command_response_cache_resp2 = collectCachedResponse(caching_client);
5455+
} else {
5456+
server.command_response_cache_resp3 = collectCachedResponse(caching_client);
5457+
}
5458+
5459+
deleteCachedResponseClient(caching_client);
5460+
}
5461+
5462+
void commandCommand(client *c) {
5463+
/* Use cached response if available for the client's protocol version */
5464+
sds *cache = (c->resp == 2) ? &server.command_response_cache_resp2 : &server.command_response_cache_resp3;
5465+
5466+
if (*cache == NULL) {
5467+
cacheCommandResponse(c->resp);
5468+
}
5469+
5470+
addReplyProto(c, *cache, sdslen(*cache));
53845471
}
53855472

53865473
/* COMMAND COUNT */

src/server.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1677,6 +1677,8 @@ struct valkeyServer {
16771677
serverDb **db; /* each db created when it's first used */
16781678
hashtable *commands; /* Command table */
16791679
hashtable *orig_commands; /* Command table before command renaming. */
1680+
sds command_response_cache_resp2; /* Cached COMMAND response for RESP2 */
1681+
sds command_response_cache_resp3; /* Cached COMMAND response for RESP3 */
16801682
aeEventLoop *el;
16811683
_Atomic AeIoState io_poll_state; /* Indicates the state of the IO polling. */
16821684
int io_ae_fired_events; /* Number of poll events received by the IO thread. */
@@ -2626,6 +2628,8 @@ struct serverCommand {
26262628
* (not the fullname), and the value is the serverCommand structure pointer. */
26272629
struct serverCommand *parent;
26282630
struct ValkeyModuleCommand *module_cmd; /* A pointer to the module command data (NULL if native command) */
2631+
sds info_cache_resp2; /* Cached COMMAND INFO response for RESP2 */
2632+
sds info_cache_resp3; /* Cached COMMAND INFO response for RESP3 */
26292633
};
26302634

26312635
struct serverError {
@@ -3783,6 +3787,7 @@ void commandCommand(client *c);
37833787
void commandCountCommand(client *c);
37843788
void commandListCommand(client *c);
37853789
void commandInfoCommand(client *c);
3790+
void invalidateCommandCache(void);
37863791
void commandGetKeysCommand(client *c);
37873792
void commandGetKeysAndFlagsCommand(client *c);
37883793
void commandHelpCommand(client *c);

0 commit comments

Comments
 (0)