@@ -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. */
52265290void 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 */
0 commit comments