Replace dict with hashtable in command tables (#1065)

This changes the type of command tables from dict to hashtable. Command
table lookup takes ~3% of overall CPU time in benchmarks, so it is a
good candidate for optimization.

My initial SET benchmark comparison suggests that hashtable is about 4.5
times faster than dict and this replacement reduced overall CPU time by
2.79% 🥳

---------

Signed-off-by: Rain Valentine <rainval@amazon.com>
Signed-off-by: Rain Valentine <rsg000@gmail.com>
Signed-off-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
Co-authored-by: Rain Valentine <rainval@amazon.com>
This commit is contained in:
Rain Valentine 2024-11-18 10:36:56 +01:00 committed by Viktor Söderqvist
parent c8ee5c2c46
commit 4efff42f04
6 changed files with 215 additions and 192 deletions

View File

@ -652,14 +652,15 @@ void ACLChangeSelectorPerm(aclSelector *selector, struct serverCommand *cmd, int
unsigned long id = cmd->id;
ACLSetSelectorCommandBit(selector, id, allow);
ACLResetFirstArgsForCommand(selector, id);
if (cmd->subcommands_dict) {
dictEntry *de;
dictIterator *di = dictGetSafeIterator(cmd->subcommands_dict);
while ((de = dictNext(di)) != NULL) {
struct serverCommand *sub = (struct serverCommand *)dictGetVal(de);
if (cmd->subcommands_ht) {
hashtableIterator iter;
hashtableInitSafeIterator(&iter, cmd->subcommands_ht);
void *next;
while (hashtableNext(&iter, &next)) {
struct serverCommand *sub = next;
ACLSetSelectorCommandBit(selector, sub->id, allow);
}
dictReleaseIterator(di);
hashtableResetIterator(&iter);
}
}
@ -669,19 +670,20 @@ void ACLChangeSelectorPerm(aclSelector *selector, struct serverCommand *cmd, int
* value. Since the category passed by the user may be non existing, the
* function returns C_ERR if the category was not found, or C_OK if it was
* found and the operation was performed. */
void ACLSetSelectorCommandBitsForCategory(dict *commands, aclSelector *selector, uint64_t cflag, int value) {
dictIterator *di = dictGetIterator(commands);
dictEntry *de;
while ((de = dictNext(di)) != NULL) {
struct serverCommand *cmd = dictGetVal(de);
void ACLSetSelectorCommandBitsForCategory(hashtable *commands, aclSelector *selector, uint64_t cflag, int value) {
hashtableIterator iter;
hashtableInitIterator(&iter, commands);
void *next;
while (hashtableNext(&iter, &next)) {
struct serverCommand *cmd = next;
if (cmd->acl_categories & cflag) {
ACLChangeSelectorPerm(selector, cmd, value);
}
if (cmd->subcommands_dict) {
ACLSetSelectorCommandBitsForCategory(cmd->subcommands_dict, selector, cflag, value);
if (cmd->subcommands_ht) {
ACLSetSelectorCommandBitsForCategory(cmd->subcommands_ht, selector, cflag, value);
}
}
dictReleaseIterator(di);
hashtableResetIterator(&iter);
}
/* This function is responsible for recomputing the command bits for all selectors of the existing users.
@ -732,26 +734,27 @@ int ACLSetSelectorCategory(aclSelector *selector, const char *category, int allo
return C_OK;
}
void ACLCountCategoryBitsForCommands(dict *commands,
void ACLCountCategoryBitsForCommands(hashtable *commands,
aclSelector *selector,
unsigned long *on,
unsigned long *off,
uint64_t cflag) {
dictIterator *di = dictGetIterator(commands);
dictEntry *de;
while ((de = dictNext(di)) != NULL) {
struct serverCommand *cmd = dictGetVal(de);
hashtableIterator iter;
hashtableInitIterator(&iter, commands);
void *next;
while (hashtableNext(&iter, &next)) {
struct serverCommand *cmd = next;
if (cmd->acl_categories & cflag) {
if (ACLGetSelectorCommandBit(selector, cmd->id))
(*on)++;
else
(*off)++;
}
if (cmd->subcommands_dict) {
ACLCountCategoryBitsForCommands(cmd->subcommands_dict, selector, on, off, cflag);
if (cmd->subcommands_ht) {
ACLCountCategoryBitsForCommands(cmd->subcommands_ht, selector, on, off, cflag);
}
}
dictReleaseIterator(di);
hashtableResetIterator(&iter);
}
/* Return the number of commands allowed (on) and denied (off) for the user 'u'
@ -1163,7 +1166,7 @@ int ACLSetSelector(aclSelector *selector, const char *op, size_t oplen) {
return C_ERR;
}
if (cmd->subcommands_dict) {
if (cmd->subcommands_ht) {
/* If user is trying to allow a valid subcommand we can just add its unique ID */
cmd = ACLLookupCommand(op + 1);
if (cmd == NULL) {
@ -2754,22 +2757,22 @@ sds getAclErrorMessage(int acl_res, user *user, struct serverCommand *cmd, sds e
* ==========================================================================*/
/* ACL CAT category */
void aclCatWithFlags(client *c, dict *commands, uint64_t cflag, int *arraylen) {
dictEntry *de;
dictIterator *di = dictGetIterator(commands);
while ((de = dictNext(di)) != NULL) {
struct serverCommand *cmd = dictGetVal(de);
void aclCatWithFlags(client *c, hashtable *commands, uint64_t cflag, int *arraylen) {
hashtableIterator iter;
hashtableInitIterator(&iter, commands);
void *next;
while (hashtableNext(&iter, &next)) {
struct serverCommand *cmd = next;
if (cmd->acl_categories & cflag) {
addReplyBulkCBuffer(c, cmd->fullname, sdslen(cmd->fullname));
(*arraylen)++;
}
if (cmd->subcommands_dict) {
aclCatWithFlags(c, cmd->subcommands_dict, cflag, arraylen);
if (cmd->subcommands_ht) {
aclCatWithFlags(c, cmd->subcommands_ht, cflag, arraylen);
}
}
dictReleaseIterator(di);
hashtableResetIterator(&iter);
}
/* Add the formatted response from a single selector to the ACL GETUSER

View File

@ -539,7 +539,6 @@ void loadServerConfigFromString(char *config) {
loadServerConfig(argv[1], 0, NULL);
} else if (!strcasecmp(argv[0], "rename-command") && argc == 3) {
struct serverCommand *cmd = lookupCommandBySds(argv[1]);
int retval;
if (!cmd) {
err = "No such command in rename-command";
@ -548,16 +547,13 @@ void loadServerConfigFromString(char *config) {
/* If the target command name is the empty string we just
* remove it from the command table. */
retval = dictDelete(server.commands, argv[1]);
serverAssert(retval == DICT_OK);
serverAssert(hashtableDelete(server.commands, argv[1]));
/* Otherwise we re-add the command under a different name. */
if (sdslen(argv[2]) != 0) {
sds copy = sdsdup(argv[2]);
retval = dictAdd(server.commands, copy, cmd);
if (retval != DICT_OK) {
sdsfree(copy);
sdsfree(cmd->fullname);
cmd->fullname = sdsdup(argv[2]);
if (!hashtableAdd(server.commands, cmd)) {
err = "Target command name already exists";
goto loaderr;
}

View File

@ -526,13 +526,12 @@ void fillCommandCDF(client *c, struct hdr_histogram *histogram) {
/* latencyCommand() helper to produce for all commands,
* a per command cumulative distribution of latencies. */
void latencyAllCommandsFillCDF(client *c, dict *commands, int *command_with_data) {
dictIterator *di = dictGetSafeIterator(commands);
dictEntry *de;
struct serverCommand *cmd;
while ((de = dictNext(di)) != NULL) {
cmd = (struct serverCommand *)dictGetVal(de);
void latencyAllCommandsFillCDF(client *c, hashtable *commands, int *command_with_data) {
hashtableIterator iter;
hashtableInitSafeIterator(&iter, commands);
void *next;
while (hashtableNext(&iter, &next)) {
struct serverCommand *cmd = next;
if (cmd->latency_histogram) {
addReplyBulkCBuffer(c, cmd->fullname, sdslen(cmd->fullname));
fillCommandCDF(c, cmd->latency_histogram);
@ -540,10 +539,10 @@ void latencyAllCommandsFillCDF(client *c, dict *commands, int *command_with_data
}
if (cmd->subcommands) {
latencyAllCommandsFillCDF(c, cmd->subcommands_dict, command_with_data);
latencyAllCommandsFillCDF(c, cmd->subcommands_ht, command_with_data);
}
}
dictReleaseIterator(di);
hashtableResetIterator(&iter);
}
/* latencyCommand() helper to produce for a specific command set,
@ -564,19 +563,19 @@ void latencySpecificCommandsFillCDF(client *c) {
command_with_data++;
}
if (cmd->subcommands_dict) {
dictEntry *de;
dictIterator *di = dictGetSafeIterator(cmd->subcommands_dict);
while ((de = dictNext(di)) != NULL) {
struct serverCommand *sub = dictGetVal(de);
if (cmd->subcommands_ht) {
hashtableIterator iter;
hashtableInitSafeIterator(&iter, cmd->subcommands_ht);
void *next;
while (hashtableNext(&iter, &next)) {
struct serverCommand *sub = next;
if (sub->latency_histogram) {
addReplyBulkCBuffer(c, sub->fullname, sdslen(sub->fullname));
fillCommandCDF(c, sub->latency_histogram);
command_with_data++;
}
}
dictReleaseIterator(di);
hashtableResetIterator(&iter);
}
}
setDeferredMapLen(c, replylen, command_with_data);

View File

@ -1298,8 +1298,8 @@ int VM_CreateCommand(ValkeyModuleCtx *ctx,
cp->serverCmd->arity = cmdfunc ? -1 : -2; /* Default value, can be changed later via dedicated API */
/* Drain IO queue before modifying commands dictionary to prevent concurrent access while modifying it. */
drainIOThreadsQueue();
serverAssert(dictAdd(server.commands, sdsdup(declared_name), cp->serverCmd) == DICT_OK);
serverAssert(dictAdd(server.orig_commands, sdsdup(declared_name), cp->serverCmd) == DICT_OK);
serverAssert(hashtableAdd(server.commands, cp->serverCmd));
serverAssert(hashtableAdd(server.orig_commands, cp->serverCmd));
cp->serverCmd->id = ACLGetCommandID(declared_name); /* ID used for ACL. */
return VALKEYMODULE_OK;
}
@ -1431,7 +1431,7 @@ int VM_CreateSubcommand(ValkeyModuleCommand *parent,
/* Check if the command name is busy within the parent command. */
sds declared_name = sdsnew(name);
if (parent_cmd->subcommands_dict && lookupSubcommand(parent_cmd, declared_name) != NULL) {
if (parent_cmd->subcommands_ht && lookupSubcommand(parent_cmd, declared_name) != NULL) {
sdsfree(declared_name);
return VALKEYMODULE_ERR;
}
@ -1441,7 +1441,7 @@ int VM_CreateSubcommand(ValkeyModuleCommand *parent,
moduleCreateCommandProxy(parent->module, declared_name, fullname, cmdfunc, flags, firstkey, lastkey, keystep);
cp->serverCmd->arity = -2;
commandAddSubcommand(parent_cmd, cp->serverCmd, name);
commandAddSubcommand(parent_cmd, cp->serverCmd);
return VALKEYMODULE_OK;
}
@ -12080,20 +12080,21 @@ int moduleFreeCommand(struct ValkeyModule *module, struct serverCommand *cmd) {
moduleFreeArgs(cmd->args, cmd->num_args);
zfree(cp);
if (cmd->subcommands_dict) {
dictEntry *de;
dictIterator *di = dictGetSafeIterator(cmd->subcommands_dict);
while ((de = dictNext(di)) != NULL) {
struct serverCommand *sub = dictGetVal(de);
if (cmd->subcommands_ht) {
hashtableIterator iter;
void *next;
hashtableInitSafeIterator(&iter, cmd->subcommands_ht);
while (hashtableNext(&iter, &next)) {
struct serverCommand *sub = next;
if (moduleFreeCommand(module, sub) != C_OK) continue;
serverAssert(dictDelete(cmd->subcommands_dict, sub->declared_name) == DICT_OK);
serverAssert(hashtableDelete(cmd->subcommands_ht, sub->declared_name));
sdsfree((sds)sub->declared_name);
sdsfree(sub->fullname);
zfree(sub);
}
dictReleaseIterator(di);
dictRelease(cmd->subcommands_dict);
hashtableResetIterator(&iter);
hashtableRelease(cmd->subcommands_ht);
}
return C_OK;
@ -12103,19 +12104,20 @@ void moduleUnregisterCommands(struct ValkeyModule *module) {
/* Drain IO queue before modifying commands dictionary to prevent concurrent access while modifying it. */
drainIOThreadsQueue();
/* Unregister all the commands registered by this module. */
dictIterator *di = dictGetSafeIterator(server.commands);
dictEntry *de;
while ((de = dictNext(di)) != NULL) {
struct serverCommand *cmd = dictGetVal(de);
hashtableIterator iter;
void *next;
hashtableInitSafeIterator(&iter, server.commands);
while (hashtableNext(&iter, &next)) {
struct serverCommand *cmd = next;
if (moduleFreeCommand(module, cmd) != C_OK) continue;
serverAssert(dictDelete(server.commands, cmd->fullname) == DICT_OK);
serverAssert(dictDelete(server.orig_commands, cmd->fullname) == DICT_OK);
serverAssert(hashtableDelete(server.commands, cmd->fullname));
serverAssert(hashtableDelete(server.orig_commands, cmd->fullname));
sdsfree((sds)cmd->declared_name);
sdsfree(cmd->fullname);
zfree(cmd);
}
dictReleaseIterator(di);
hashtableResetIterator(&iter);
}
/* We parse argv to add sds "NAME VALUE" pairs to the server.module_configs_queue list of configs.

View File

@ -390,6 +390,11 @@ int dictSdsKeyCaseCompare(const void *key1, const void *key2) {
return strcasecmp(key1, key2) == 0;
}
/* Case insensitive key comparison */
int hashtableStringKeyCaseCompare(const void *key1, const void *key2) {
return strcasecmp(key1, key2);
}
void dictObjectDestructor(void *val) {
if (val == NULL) return; /* Lazy freeing will set value to NULL. */
decrRefCount(val);
@ -506,6 +511,16 @@ int dictResizeAllowed(size_t moreMem, double usedRatio) {
}
}
const void *hashtableCommandGetKey(const void *element) {
struct serverCommand *command = (struct serverCommand *)element;
return command->fullname;
}
const void *hashtableSubcommandGetKey(const void *element) {
struct serverCommand *command = (struct serverCommand *)element;
return command->declared_name;
}
/* Generic hash table type where keys are Objects, Values
* dummy pointers. */
dictType objectKeyPointerValueDictType = {
@ -578,16 +593,17 @@ dictType kvstoreExpiresDictType = {
kvstoreDictMetadataSize,
};
/* Command table. sds string -> command struct pointer. */
dictType commandTableDictType = {
dictSdsCaseHash, /* hash function */
NULL, /* key dup */
dictSdsKeyCaseCompare, /* key compare */
dictSdsDestructor, /* key destructor */
NULL, /* val destructor */
NULL, /* allow to expand */
.no_incremental_rehash = 1, /* no incremental rehash as the command table may be accessed from IO threads. */
};
/* Command set, hashed by sds string, stores serverCommand structs. */
hashtableType commandSetType = {.entryGetKey = hashtableCommandGetKey,
.hashFunction = dictSdsCaseHash,
.keyCompare = hashtableStringKeyCaseCompare,
.instant_rehashing = 1};
/* Sub-command set, hashed by char* string, stores serverCommand structs. */
hashtableType subcommandSetType = {.entryGetKey = hashtableSubcommandGetKey,
.hashFunction = dictCStrCaseHash,
.keyCompare = hashtableStringKeyCaseCompare,
.instant_rehashing = 1};
/* Hash type hash table (note that small hashes are represented with listpacks) */
dictType hashDictType = {
@ -2177,8 +2193,8 @@ void initServerConfig(void) {
/* Command table -- we initialize it here as it is part of the
* initial configuration, since command names may be changed via
* valkey.conf using the rename-command directive. */
server.commands = dictCreate(&commandTableDictType);
server.orig_commands = dictCreate(&commandTableDictType);
server.commands = hashtableCreate(&commandSetType);
server.orig_commands = hashtableCreate(&commandSetType);
populateCommandTable();
/* Debugging */
@ -3017,13 +3033,13 @@ sds catSubCommandFullname(const char *parent_name, const char *sub_name) {
return sdscatfmt(sdsempty(), "%s|%s", parent_name, sub_name);
}
void commandAddSubcommand(struct serverCommand *parent, struct serverCommand *subcommand, const char *declared_name) {
if (!parent->subcommands_dict) parent->subcommands_dict = dictCreate(&commandTableDictType);
void commandAddSubcommand(struct serverCommand *parent, struct serverCommand *subcommand) {
if (!parent->subcommands_ht) parent->subcommands_ht = hashtableCreate(&subcommandSetType);
subcommand->parent = parent; /* Assign the parent command */
subcommand->id = ACLGetCommandID(subcommand->fullname); /* Assign the ID used for ACL. */
serverAssert(dictAdd(parent->subcommands_dict, sdsnew(declared_name), subcommand) == DICT_OK);
serverAssert(hashtableAdd(parent->subcommands_ht, subcommand));
}
/* Set implicit ACl categories (see comment above the definition of
@ -3075,7 +3091,7 @@ int populateCommandStructure(struct serverCommand *c) {
sub->fullname = catSubCommandFullname(c->declared_name, sub->declared_name);
if (populateCommandStructure(sub) == C_ERR) continue;
commandAddSubcommand(c, sub, sub->declared_name);
commandAddSubcommand(c, sub);
}
}
@ -3099,22 +3115,20 @@ void populateCommandTable(void) {
c->fullname = sdsnew(c->declared_name);
if (populateCommandStructure(c) == C_ERR) continue;
retval1 = dictAdd(server.commands, sdsdup(c->fullname), c);
retval1 = hashtableAdd(server.commands, c);
/* Populate an additional dictionary that will be unaffected
* by rename-command statements in valkey.conf. */
retval2 = dictAdd(server.orig_commands, sdsdup(c->fullname), c);
serverAssert(retval1 == DICT_OK && retval2 == DICT_OK);
retval2 = hashtableAdd(server.orig_commands, c);
serverAssert(retval1 && retval2);
}
}
void resetCommandTableStats(dict *commands) {
struct serverCommand *c;
dictEntry *de;
dictIterator *di;
di = dictGetSafeIterator(commands);
while ((de = dictNext(di)) != NULL) {
c = (struct serverCommand *)dictGetVal(de);
void resetCommandTableStats(hashtable *commands) {
hashtableIterator iter;
void *next;
hashtableInitSafeIterator(&iter, commands);
while (hashtableNext(&iter, &next)) {
struct serverCommand *c = next;
c->microseconds = 0;
c->calls = 0;
c->rejected_calls = 0;
@ -3123,9 +3137,9 @@ void resetCommandTableStats(dict *commands) {
hdr_close(c->latency_histogram);
c->latency_histogram = NULL;
}
if (c->subcommands_dict) resetCommandTableStats(c->subcommands_dict);
if (c->subcommands_ht) resetCommandTableStats(c->subcommands_ht);
}
dictReleaseIterator(di);
hashtableResetIterator(&iter);
}
void resetErrorTableStats(void) {
@ -3172,13 +3186,18 @@ void serverOpArrayFree(serverOpArray *oa) {
/* ====================== Commands lookup and execution ===================== */
int isContainerCommandBySds(sds s) {
struct serverCommand *base_cmd = dictFetchValue(server.commands, s);
int has_subcommands = base_cmd && base_cmd->subcommands_dict;
void *entry;
int found_command = hashtableFind(server.commands, s, &entry);
struct serverCommand *base_cmd = entry;
int has_subcommands = found_command && base_cmd->subcommands_ht;
return has_subcommands;
}
struct serverCommand *lookupSubcommand(struct serverCommand *container, sds sub_name) {
return dictFetchValue(container->subcommands_dict, sub_name);
void *entry = NULL;
hashtableFind(container->subcommands_ht, sub_name, &entry);
struct serverCommand *subcommand = entry;
return subcommand;
}
/* Look up a command by argv and argc
@ -3189,9 +3208,11 @@ struct serverCommand *lookupSubcommand(struct serverCommand *container, sds sub_
* name (e.g. in COMMAND INFO) rather than to find the command
* a user requested to execute (in processCommand).
*/
struct serverCommand *lookupCommandLogic(dict *commands, robj **argv, int argc, int strict) {
struct serverCommand *base_cmd = dictFetchValue(commands, argv[0]->ptr);
int has_subcommands = base_cmd && base_cmd->subcommands_dict;
struct serverCommand *lookupCommandLogic(hashtable *commands, robj **argv, int argc, int strict) {
void *entry = NULL;
int found_command = hashtableFind(commands, argv[0]->ptr, &entry);
struct serverCommand *base_cmd = entry;
int has_subcommands = found_command && base_cmd->subcommands_ht;
if (argc == 1 || !has_subcommands) {
if (strict && argc != 1) return NULL;
/* Note: It is possible that base_cmd->proc==NULL (e.g. CONFIG) */
@ -3207,7 +3228,7 @@ struct serverCommand *lookupCommand(robj **argv, int argc) {
return lookupCommandLogic(server.commands, argv, argc, 0);
}
struct serverCommand *lookupCommandBySdsLogic(dict *commands, sds s) {
struct serverCommand *lookupCommandBySdsLogic(hashtable *commands, sds s) {
int argc, j;
sds *strings = sdssplitlen(s, sdslen(s), "|", 1, &argc);
if (strings == NULL) return NULL;
@ -3234,7 +3255,7 @@ struct serverCommand *lookupCommandBySds(sds s) {
return lookupCommandBySdsLogic(server.commands, s);
}
struct serverCommand *lookupCommandByCStringLogic(dict *commands, const char *s) {
struct serverCommand *lookupCommandByCStringLogic(hashtable *commands, const char *s) {
struct serverCommand *cmd;
sds name = sdsnew(s);
@ -4877,23 +4898,25 @@ void addReplyCommandSubCommands(client *c,
struct serverCommand *cmd,
void (*reply_function)(client *, struct serverCommand *),
int use_map) {
if (!cmd->subcommands_dict) {
if (!cmd->subcommands_ht) {
addReplySetLen(c, 0);
return;
}
if (use_map)
addReplyMapLen(c, dictSize(cmd->subcommands_dict));
addReplyMapLen(c, hashtableSize(cmd->subcommands_ht));
else
addReplyArrayLen(c, dictSize(cmd->subcommands_dict));
dictEntry *de;
dictIterator *di = dictGetSafeIterator(cmd->subcommands_dict);
while ((de = dictNext(di)) != NULL) {
struct serverCommand *sub = (struct serverCommand *)dictGetVal(de);
addReplyArrayLen(c, hashtableSize(cmd->subcommands_ht));
void *next;
hashtableIterator iter;
hashtableInitSafeIterator(&iter, cmd->subcommands_ht);
while (hashtableNext(&iter, &next)) {
struct serverCommand *sub = next;
if (use_map) addReplyBulkCBuffer(c, sub->fullname, sdslen(sub->fullname));
reply_function(c, sub);
}
dictReleaseIterator(di);
hashtableResetIterator(&iter);
}
/* Output the representation of a server command. Used by the COMMAND command and COMMAND INFO. */
@ -4939,7 +4962,7 @@ void addReplyCommandDocs(client *c, struct serverCommand *cmd) {
if (cmd->reply_schema) maplen++;
#endif
if (cmd->args) maplen++;
if (cmd->subcommands_dict) maplen++;
if (cmd->subcommands_ht) maplen++;
addReplyMapLen(c, maplen);
if (cmd->summary) {
@ -4989,7 +5012,7 @@ void addReplyCommandDocs(client *c, struct serverCommand *cmd) {
addReplyBulkCString(c, "arguments");
addReplyCommandArgList(c, cmd->args, cmd->num_args);
}
if (cmd->subcommands_dict) {
if (cmd->subcommands_ht) {
addReplyBulkCString(c, "subcommands");
addReplyCommandSubCommands(c, cmd, addReplyCommandDocs, 1);
}
@ -5046,20 +5069,20 @@ void getKeysSubcommand(client *c) {
/* COMMAND (no args) */
void commandCommand(client *c) {
dictIterator *di;
dictEntry *de;
addReplyArrayLen(c, dictSize(server.commands));
di = dictGetIterator(server.commands);
while ((de = dictNext(di)) != NULL) {
addReplyCommandInfo(c, dictGetVal(de));
hashtableIterator iter;
void *next;
addReplyArrayLen(c, hashtableSize(server.commands));
hashtableInitIterator(&iter, server.commands);
while (hashtableNext(&iter, &next)) {
struct serverCommand *cmd = next;
addReplyCommandInfo(c, cmd);
}
dictReleaseIterator(di);
hashtableResetIterator(&iter);
}
/* COMMAND COUNT */
void commandCountCommand(client *c) {
addReplyLongLong(c, dictSize(server.commands));
addReplyLongLong(c, hashtableSize(server.commands));
}
typedef enum {
@ -5105,39 +5128,39 @@ int shouldFilterFromCommandList(struct serverCommand *cmd, commandListFilter *fi
}
/* COMMAND LIST FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>) */
void commandListWithFilter(client *c, dict *commands, commandListFilter filter, int *numcmds) {
dictEntry *de;
dictIterator *di = dictGetIterator(commands);
while ((de = dictNext(di)) != NULL) {
struct serverCommand *cmd = dictGetVal(de);
void commandListWithFilter(client *c, hashtable *commands, commandListFilter filter, int *numcmds) {
hashtableIterator iter;
void *next;
hashtableInitIterator(&iter, commands);
while (hashtableNext(&iter, &next)) {
struct serverCommand *cmd = next;
if (!shouldFilterFromCommandList(cmd, &filter)) {
addReplyBulkCBuffer(c, cmd->fullname, sdslen(cmd->fullname));
(*numcmds)++;
}
if (cmd->subcommands_dict) {
commandListWithFilter(c, cmd->subcommands_dict, filter, numcmds);
if (cmd->subcommands_ht) {
commandListWithFilter(c, cmd->subcommands_ht, filter, numcmds);
}
}
dictReleaseIterator(di);
hashtableResetIterator(&iter);
}
/* COMMAND LIST */
void commandListWithoutFilter(client *c, dict *commands, int *numcmds) {
dictEntry *de;
dictIterator *di = dictGetIterator(commands);
while ((de = dictNext(di)) != NULL) {
struct serverCommand *cmd = dictGetVal(de);
void commandListWithoutFilter(client *c, hashtable *commands, int *numcmds) {
hashtableIterator iter;
void *next;
hashtableInitIterator(&iter, commands);
while (hashtableNext(&iter, &next)) {
struct serverCommand *cmd = next;
addReplyBulkCBuffer(c, cmd->fullname, sdslen(cmd->fullname));
(*numcmds)++;
if (cmd->subcommands_dict) {
commandListWithoutFilter(c, cmd->subcommands_dict, numcmds);
if (cmd->subcommands_ht) {
commandListWithoutFilter(c, cmd->subcommands_ht, numcmds);
}
}
dictReleaseIterator(di);
hashtableResetIterator(&iter);
}
/* COMMAND LIST [FILTERBY (MODULE <module-name>|ACLCAT <cat>|PATTERN <pattern>)] */
@ -5186,14 +5209,15 @@ void commandInfoCommand(client *c) {
int i;
if (c->argc == 2) {
dictIterator *di;
dictEntry *de;
addReplyArrayLen(c, dictSize(server.commands));
di = dictGetIterator(server.commands);
while ((de = dictNext(di)) != NULL) {
addReplyCommandInfo(c, dictGetVal(de));
hashtableIterator iter;
void *next;
addReplyArrayLen(c, hashtableSize(server.commands));
hashtableInitIterator(&iter, server.commands);
while (hashtableNext(&iter, &next)) {
struct serverCommand *cmd = next;
addReplyCommandInfo(c, cmd);
}
dictReleaseIterator(di);
hashtableResetIterator(&iter);
} else {
addReplyArrayLen(c, c->argc - 2);
for (i = 2; i < c->argc; i++) {
@ -5207,16 +5231,16 @@ void commandDocsCommand(client *c) {
int i;
if (c->argc == 2) {
/* Reply with an array of all commands */
dictIterator *di;
dictEntry *de;
addReplyMapLen(c, dictSize(server.commands));
di = dictGetIterator(server.commands);
while ((de = dictNext(di)) != NULL) {
struct serverCommand *cmd = dictGetVal(de);
hashtableIterator iter;
void *next;
addReplyMapLen(c, hashtableSize(server.commands));
hashtableInitIterator(&iter, server.commands);
while (hashtableNext(&iter, &next)) {
struct serverCommand *cmd = next;
addReplyBulkCBuffer(c, cmd->fullname, sdslen(cmd->fullname));
addReplyCommandDocs(c, cmd);
}
dictReleaseIterator(di);
hashtableResetIterator(&iter);
} else {
/* Reply with an array of the requested commands (if we find them) */
int numcmds = 0;
@ -5336,14 +5360,13 @@ const char *getSafeInfoString(const char *s, size_t len, char **tmp) {
return memmapchars(new, len, unsafe_info_chars, unsafe_info_chars_substs, sizeof(unsafe_info_chars) - 1);
}
sds genValkeyInfoStringCommandStats(sds info, dict *commands) {
struct serverCommand *c;
dictEntry *de;
dictIterator *di;
di = dictGetSafeIterator(commands);
while ((de = dictNext(di)) != NULL) {
sds genValkeyInfoStringCommandStats(sds info, hashtable *commands) {
hashtableIterator iter;
void *next;
hashtableInitSafeIterator(&iter, commands);
while (hashtableNext(&iter, &next)) {
struct serverCommand *c = next;
char *tmpsafe;
c = (struct serverCommand *)dictGetVal(de);
if (c->calls || c->failed_calls || c->rejected_calls) {
info = sdscatprintf(info,
"cmdstat_%s:calls=%lld,usec=%lld,usec_per_call=%.2f"
@ -5353,11 +5376,11 @@ sds genValkeyInfoStringCommandStats(sds info, dict *commands) {
c->rejected_calls, c->failed_calls);
if (tmpsafe != NULL) zfree(tmpsafe);
}
if (c->subcommands_dict) {
info = genValkeyInfoStringCommandStats(info, c->subcommands_dict);
if (c->subcommands_ht) {
info = genValkeyInfoStringCommandStats(info, c->subcommands_ht);
}
}
dictReleaseIterator(di);
hashtableResetIterator(&iter);
return info;
}
@ -5374,24 +5397,23 @@ sds genValkeyInfoStringACLStats(sds info) {
return info;
}
sds genValkeyInfoStringLatencyStats(sds info, dict *commands) {
struct serverCommand *c;
dictEntry *de;
dictIterator *di;
di = dictGetSafeIterator(commands);
while ((de = dictNext(di)) != NULL) {
sds genValkeyInfoStringLatencyStats(sds info, hashtable *commands) {
hashtableIterator iter;
void *next;
hashtableInitSafeIterator(&iter, commands);
while (hashtableNext(&iter, &next)) {
struct serverCommand *c = next;
char *tmpsafe;
c = (struct serverCommand *)dictGetVal(de);
if (c->latency_histogram) {
info = fillPercentileDistributionLatencies(
info, getSafeInfoString(c->fullname, sdslen(c->fullname), &tmpsafe), c->latency_histogram);
if (tmpsafe != NULL) zfree(tmpsafe);
}
if (c->subcommands_dict) {
info = genValkeyInfoStringLatencyStats(info, c->subcommands_dict);
if (c->subcommands_ht) {
info = genValkeyInfoStringLatencyStats(info, c->subcommands_ht);
}
}
dictReleaseIterator(di);
hashtableResetIterator(&iter);
return info;
}

View File

@ -67,7 +67,8 @@ typedef long long ustime_t; /* microsecond time type. */
#include "ae.h" /* Event driven programming library */
#include "sds.h" /* Dynamic safe strings */
#include "dict.h" /* Hash tables */
#include "dict.h" /* Hash tables (old implementation) */
#include "hashtable.h" /* Hash tables (new implementation) */
#include "kvstore.h" /* Slot-based hash table */
#include "adlist.h" /* Linked lists */
#include "zmalloc.h" /* total memory usage aware version of malloc/free */
@ -1693,8 +1694,8 @@ struct valkeyServer {
int hz; /* serverCron() calls frequency in hertz */
int in_fork_child; /* indication that this is a fork child */
serverDb *db;
dict *commands; /* Command table */
dict *orig_commands; /* Command table before command renaming. */
hashtable *commands; /* Command table */
hashtable *orig_commands; /* Command table before command renaming. */
aeEventLoop *el;
_Atomic AeIoState io_poll_state; /* Indicates the state of the IO polling. */
int io_ae_fired_events; /* Number of poll events received by the IO thread. */
@ -2577,12 +2578,12 @@ struct serverCommand {
bit set in the bitmap of allowed commands. */
sds fullname; /* A SDS string representing the command fullname. */
struct hdr_histogram
*latency_histogram; /*points to the command latency command histogram (unit of time nanosecond) */
*latency_histogram; /* Points to the command latency command histogram (unit of time nanosecond). */
keySpec legacy_range_key_spec; /* The legacy (first,last,step) key spec is
* still maintained (if applicable) so that
* we can still support the reply format of
* COMMAND INFO and COMMAND GETKEYS */
dict *subcommands_dict; /* A dictionary that holds the subcommands, the key is the subcommand sds name
hashtable *subcommands_ht; /* Subcommands hash table. The key is the subcommand sds name
* (not the fullname), and the value is the serverCommand structure pointer. */
struct serverCommand *parent;
struct ValkeyModuleCommand *module_cmd; /* A pointer to the module command data (NULL if native command) */
@ -3311,9 +3312,9 @@ connListener *listenerByType(const char *typename);
int changeListener(connListener *listener);
struct serverCommand *lookupSubcommand(struct serverCommand *container, sds sub_name);
struct serverCommand *lookupCommand(robj **argv, int argc);
struct serverCommand *lookupCommandBySdsLogic(dict *commands, sds s);
struct serverCommand *lookupCommandBySdsLogic(hashtable *commands, sds s);
struct serverCommand *lookupCommandBySds(sds s);
struct serverCommand *lookupCommandByCStringLogic(dict *commands, const char *s);
struct serverCommand *lookupCommandByCStringLogic(hashtable *commands, const char *s);
struct serverCommand *lookupCommandByCString(const char *s);
struct serverCommand *lookupCommandOrOriginal(robj **argv, int argc);
int commandCheckExistence(client *c, sds *err);
@ -3347,7 +3348,7 @@ void serverLogRawFromHandler(int level, const char *msg);
void usage(void);
void updateDictResizePolicy(void);
void populateCommandTable(void);
void resetCommandTableStats(dict *commands);
void resetCommandTableStats(hashtable *commands);
void resetErrorTableStats(void);
void adjustOpenFilesLimit(void);
void incrementErrorCount(const char *fullerr, size_t namelen);
@ -4045,7 +4046,7 @@ int memtest_preserving_test(unsigned long *m, size_t bytes, int passes);
void mixDigest(unsigned char *digest, const void *ptr, size_t len);
void xorDigest(unsigned char *digest, const void *ptr, size_t len);
sds catSubCommandFullname(const char *parent_name, const char *sub_name);
void commandAddSubcommand(struct serverCommand *parent, struct serverCommand *subcommand, const char *declared_name);
void commandAddSubcommand(struct serverCommand *parent, struct serverCommand *subcommand);
void debugDelay(int usec);
void killThreads(void);
void makeThreadKillable(void);