diff --git a/src/acl.c b/src/acl.c index 688820fd89..52263185eb 100644 --- a/src/acl.c +++ b/src/acl.c @@ -652,14 +652,14 @@ 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_set) { + hashsetIterator iter; + hashsetInitSafeIterator(&iter, cmd->subcommands_set); + struct serverCommand *sub; + while (hashsetNext(&iter, (void **)&sub)) { ACLSetSelectorCommandBit(selector, sub->id, allow); } - dictReleaseIterator(di); + hashsetResetIterator(&iter); } } @@ -669,19 +669,19 @@ 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(hashset *commands, aclSelector *selector, uint64_t cflag, int value) { + hashsetIterator iter; + hashsetInitIterator(&iter, commands); + struct serverCommand *cmd; + while (hashsetNext(&iter, (void **)&cmd)) { if (cmd->acl_categories & cflag) { ACLChangeSelectorPerm(selector, cmd, value); } - if (cmd->subcommands_dict) { - ACLSetSelectorCommandBitsForCategory(cmd->subcommands_dict, selector, cflag, value); + if (cmd->subcommands_set) { + ACLSetSelectorCommandBitsForCategory(cmd->subcommands_set, selector, cflag, value); } } - dictReleaseIterator(di); + hashsetResetIterator(&iter); } /* This function is responsible for recomputing the command bits for all selectors of the existing users. @@ -732,26 +732,26 @@ int ACLSetSelectorCategory(aclSelector *selector, const char *category, int allo return C_OK; } -void ACLCountCategoryBitsForCommands(dict *commands, +void ACLCountCategoryBitsForCommands(hashset *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); + hashsetIterator iter; + hashsetInitIterator(&iter, commands); + struct serverCommand *cmd; + while (hashsetNext(&iter, (void **)&cmd)) { 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_set) { + ACLCountCategoryBitsForCommands(cmd->subcommands_set, selector, on, off, cflag); } } - dictReleaseIterator(di); + hashsetResetIterator(&iter); } /* Return the number of commands allowed (on) and denied (off) for the user 'u' @@ -1163,7 +1163,7 @@ int ACLSetSelector(aclSelector *selector, const char *op, size_t oplen) { return C_ERR; } - if (cmd->subcommands_dict) { + if (cmd->subcommands_set) { /* 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 +2754,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); +void aclCatWithFlags(client *c, hashset *commands, uint64_t cflag, int *arraylen) { + hashsetIterator iter; + hashsetInitIterator(&iter, commands); - while ((de = dictNext(di)) != NULL) { - struct serverCommand *cmd = dictGetVal(de); + struct serverCommand *cmd; + while (hashsetNext(&iter, (void **)&cmd)) { 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_set) { + aclCatWithFlags(c, cmd->subcommands_set, cflag, arraylen); } } - dictReleaseIterator(di); + hashsetResetIterator(&iter); } /* Add the formatted response from a single selector to the ACL GETUSER diff --git a/src/config.c b/src/config.c index 663cf5da38..1d7fadfe73 100644 --- a/src/config.c +++ b/src/config.c @@ -532,7 +532,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"; @@ -541,16 +540,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(hashsetDelete(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 (!hashsetAdd(server.commands, cmd)) { err = "Target command name already exists"; goto loaderr; } diff --git a/src/latency.c b/src/latency.c index eef1532d03..ee95859271 100644 --- a/src/latency.c +++ b/src/latency.c @@ -527,13 +527,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; +void latencyAllCommandsFillCDF(client *c, hashset *commands, int *command_with_data) { + hashsetIterator iter; + hashsetInitSafeIterator(&iter, commands); struct serverCommand *cmd; - while ((de = dictNext(di)) != NULL) { - cmd = (struct serverCommand *)dictGetVal(de); + while (hashsetNext(&iter, (void **)&cmd)) { if (cmd->latency_histogram) { addReplyBulkCBuffer(c, cmd->fullname, sdslen(cmd->fullname)); fillCommandCDF(c, cmd->latency_histogram); @@ -541,10 +540,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_set, command_with_data); } } - dictReleaseIterator(di); + hashsetResetIterator(&iter); } /* latencyCommand() helper to produce for a specific command set, @@ -565,19 +564,19 @@ void latencySpecificCommandsFillCDF(client *c) { command_with_data++; } - if (cmd->subcommands_dict) { - dictEntry *de; - dictIterator *di = dictGetSafeIterator(cmd->subcommands_dict); + if (cmd->subcommands_set) { + hashsetIterator iter; + hashsetInitSafeIterator(&iter, cmd->subcommands_set); - while ((de = dictNext(di)) != NULL) { - struct serverCommand *sub = dictGetVal(de); + struct serverCommand *sub; + while (hashsetNext(&iter, (void **)&sub)) { if (sub->latency_histogram) { addReplyBulkCBuffer(c, sub->fullname, sdslen(sub->fullname)); fillCommandCDF(c, sub->latency_histogram); command_with_data++; } } - dictReleaseIterator(di); + hashsetResetIterator(&iter); } } setDeferredMapLen(c, replylen, command_with_data); diff --git a/src/module.c b/src/module.c index 2884239200..c83dcaae6b 100644 --- a/src/module.c +++ b/src/module.c @@ -1297,8 +1297,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(hashsetAdd(server.commands, cp->serverCmd)); + serverAssert(hashsetAdd(server.orig_commands, cp->serverCmd)); cp->serverCmd->id = ACLGetCommandID(declared_name); /* ID used for ACL. */ return VALKEYMODULE_OK; } @@ -1430,7 +1430,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_set && lookupSubcommand(parent_cmd, declared_name) != NULL) { sdsfree(declared_name); return VALKEYMODULE_ERR; } @@ -1440,7 +1440,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; } @@ -12059,20 +12059,20 @@ 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_set) { + hashsetIterator iter; + hashsetInitSafeIterator(&iter, cmd->subcommands_set); + struct serverCommand *sub; + while (hashsetNext(&iter, (void **)&sub)) { if (moduleFreeCommand(module, sub) != C_OK) continue; - serverAssert(dictDelete(cmd->subcommands_dict, sub->declared_name) == DICT_OK); + serverAssert(hashsetDelete(cmd->subcommands_set, sub->declared_name)); sdsfree((sds)sub->declared_name); sdsfree(sub->fullname); zfree(sub); } - dictReleaseIterator(di); - dictRelease(cmd->subcommands_dict); + hashsetResetIterator(&iter); + hashsetRelease(cmd->subcommands_set); } return C_OK; @@ -12082,19 +12082,19 @@ 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); + hashsetIterator iter; + hashsetInitSafeIterator(&iter, server.commands); + struct serverCommand *cmd; + while (hashsetNext(&iter, (void **)&cmd)) { 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(hashsetDelete(server.commands, cmd->fullname)); + serverAssert(hashsetDelete(server.orig_commands, cmd->fullname)); sdsfree((sds)cmd->declared_name); sdsfree(cmd->fullname); zfree(cmd); } - dictReleaseIterator(di); + hashsetResetIterator(&iter); } /* We parse argv to add sds "NAME VALUE" pairs to the server.module_configs_queue list of configs. diff --git a/src/server.c b/src/server.c index ab95f84346..6e2673a1fd 100644 --- a/src/server.c +++ b/src/server.c @@ -300,13 +300,18 @@ size_t dictSdsEmbedKey(unsigned char *buf, size_t buf_len, const void *key, uint return sdscopytobuffer(buf, buf_len, (sds)key, key_offset); } -/* A case insensitive version used for the command lookup table and other - * places where case insensitive non binary-safe comparison is needed. */ +/* Case insensitive non binary-safe comparison */ int dictSdsKeyCaseCompare(dict *d, const void *key1, const void *key2) { UNUSED(d); return strcasecmp(key1, key2) == 0; } +/* Case insensitive key comparison */ +int hashsetStringKeyCaseCompare(hashset *hs, const void *key1, const void *key2) { + UNUSED(hs); + return strcasecmp(key1, key2); +} + void dictObjectDestructor(dict *d, void *val) { UNUSED(d); if (val == NULL) return; /* Lazy freeing will set value to NULL. */ @@ -430,6 +435,16 @@ int dictResizeAllowed(size_t moreMem, double usedRatio) { } } +const void *hashsetCommandGetKey(const void *element) { + struct serverCommand *command = (struct serverCommand *)element; + return command->fullname; +} + +const void *hashsetSubcommandGetKey(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 = { @@ -502,16 +517,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. */ +hashsetType commandSetType = {.elementGetKey = hashsetCommandGetKey, + .hashFunction = dictSdsCaseHash, + .keyCompare = hashsetStringKeyCaseCompare, + .instant_rehashing = 1}; + +/* Command set, hashed by char* string, stores serverCommand structs. */ +hashsetType subcommandSetType = {.elementGetKey = hashsetSubcommandGetKey, + .hashFunction = dictCStrCaseHash, + .keyCompare = hashsetStringKeyCaseCompare, + .instant_rehashing = 1}; /* Hash type hash table (note that small hashes are represented with listpacks) */ dictType hashDictType = { @@ -2115,8 +2131,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 = hashsetCreate(&commandSetType); + server.orig_commands = hashsetCreate(&commandSetType); populateCommandTable(); /* Debugging */ @@ -2960,13 +2976,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_set) parent->subcommands_set = hashsetCreate(&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(hashsetAdd(parent->subcommands_set, subcommand)); } /* Set implicit ACl categories (see comment above the definition of @@ -3018,7 +3034,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); } } @@ -3042,22 +3058,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 = hashsetAdd(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 = hashsetAdd(server.orig_commands, c); + serverAssert(retval1 && retval2); } } -void resetCommandTableStats(dict *commands) { +void resetCommandTableStats(hashset *commands) { struct serverCommand *c; - dictEntry *de; - dictIterator *di; + hashsetIterator iter; - di = dictGetSafeIterator(commands); - while ((de = dictNext(di)) != NULL) { - c = (struct serverCommand *)dictGetVal(de); + hashsetInitSafeIterator(&iter, commands); + while (hashsetNext(&iter, (void **)&c)) { c->microseconds = 0; c->calls = 0; c->rejected_calls = 0; @@ -3066,9 +3080,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_set) resetCommandTableStats(c->subcommands_set); } - dictReleaseIterator(di); + hashsetResetIterator(&iter); } void resetErrorTableStats(void) { @@ -3115,13 +3129,16 @@ 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; + struct serverCommand *base_cmd; + int found_command = hashsetFind(server.commands, s, (void **)&base_cmd); + int has_subcommands = found_command && base_cmd->subcommands_set; return has_subcommands; } struct serverCommand *lookupSubcommand(struct serverCommand *container, sds sub_name) { - return dictFetchValue(container->subcommands_dict, sub_name); + struct serverCommand *subcommand = NULL; + hashsetFind(container->subcommands_set, sub_name, (void **)&subcommand); + return subcommand; } /* Look up a command by argv and argc @@ -3132,9 +3149,10 @@ 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(hashset *commands, robj **argv, int argc, int strict) { + struct serverCommand *base_cmd = NULL; + int found_command = hashsetFind(commands, argv[0]->ptr, (void **)&base_cmd); + int has_subcommands = found_command && base_cmd->subcommands_set; if (argc == 1 || !has_subcommands) { if (strict && argc != 1) return NULL; /* Note: It is possible that base_cmd->proc==NULL (e.g. CONFIG) */ @@ -3150,7 +3168,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(hashset *commands, sds s) { int argc, j; sds *strings = sdssplitlen(s, sdslen(s), "|", 1, &argc); if (strings == NULL) return NULL; @@ -3177,7 +3195,7 @@ struct serverCommand *lookupCommandBySds(sds s) { return lookupCommandBySdsLogic(server.commands, s); } -struct serverCommand *lookupCommandByCStringLogic(dict *commands, const char *s) { +struct serverCommand *lookupCommandByCStringLogic(hashset *commands, const char *s) { struct serverCommand *cmd; sds name = sdsnew(s); @@ -4809,23 +4827,24 @@ void addReplyCommandSubCommands(client *c, struct serverCommand *cmd, void (*reply_function)(client *, struct serverCommand *), int use_map) { - if (!cmd->subcommands_dict) { + if (!cmd->subcommands_set) { addReplySetLen(c, 0); return; } if (use_map) - addReplyMapLen(c, dictSize(cmd->subcommands_dict)); + addReplyMapLen(c, hashsetSize(cmd->subcommands_set)); 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, hashsetSize(cmd->subcommands_set)); + + hashsetIterator iter; + struct serverCommand *sub; + hashsetInitSafeIterator(&iter, cmd->subcommands_set); + while (hashsetNext(&iter, (void **)&sub)) { if (use_map) addReplyBulkCBuffer(c, sub->fullname, sdslen(sub->fullname)); reply_function(c, sub); } - dictReleaseIterator(di); + hashsetResetIterator(&iter); } /* Output the representation of a server command. Used by the COMMAND command and COMMAND INFO. */ @@ -4871,7 +4890,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_set) maplen++; addReplyMapLen(c, maplen); if (cmd->summary) { @@ -4921,7 +4940,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_set) { addReplyBulkCString(c, "subcommands"); addReplyCommandSubCommands(c, cmd, addReplyCommandDocs, 1); } @@ -4978,20 +4997,20 @@ void getKeysSubcommand(client *c) { /* COMMAND (no args) */ void commandCommand(client *c) { - dictIterator *di; - dictEntry *de; + hashsetIterator iter; + struct serverCommand *cmd; - addReplyArrayLen(c, dictSize(server.commands)); - di = dictGetIterator(server.commands); - while ((de = dictNext(di)) != NULL) { - addReplyCommandInfo(c, dictGetVal(de)); + addReplyArrayLen(c, hashsetSize(server.commands)); + hashsetInitIterator(&iter, server.commands); + while (hashsetNext(&iter, (void **)&cmd)) { + addReplyCommandInfo(c, cmd); } - dictReleaseIterator(di); + hashsetResetIterator(&iter); } /* COMMAND COUNT */ void commandCountCommand(client *c) { - addReplyLongLong(c, dictSize(server.commands)); + addReplyLongLong(c, hashsetSize(server.commands)); } typedef enum { @@ -5037,39 +5056,39 @@ int shouldFilterFromCommandList(struct serverCommand *cmd, commandListFilter *fi } /* COMMAND LIST FILTERBY (MODULE |ACLCAT |PATTERN ) */ -void commandListWithFilter(client *c, dict *commands, commandListFilter filter, int *numcmds) { - dictEntry *de; - dictIterator *di = dictGetIterator(commands); +void commandListWithFilter(client *c, hashset *commands, commandListFilter filter, int *numcmds) { + hashsetIterator iter; + hashsetInitIterator(&iter, commands); - while ((de = dictNext(di)) != NULL) { - struct serverCommand *cmd = dictGetVal(de); + struct serverCommand *cmd; + while (hashsetNext(&iter, (void **)&cmd)) { 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_set) { + commandListWithFilter(c, cmd->subcommands_set, filter, numcmds); } } - dictReleaseIterator(di); + hashsetResetIterator(&iter); } /* COMMAND LIST */ -void commandListWithoutFilter(client *c, dict *commands, int *numcmds) { - dictEntry *de; - dictIterator *di = dictGetIterator(commands); +void commandListWithoutFilter(client *c, hashset *commands, int *numcmds) { + hashsetIterator iter; + struct serverCommand *cmd; + hashsetInitIterator(&iter, commands); - while ((de = dictNext(di)) != NULL) { - struct serverCommand *cmd = dictGetVal(de); + while (hashsetNext(&iter, (void **)&cmd)) { addReplyBulkCBuffer(c, cmd->fullname, sdslen(cmd->fullname)); (*numcmds)++; - if (cmd->subcommands_dict) { - commandListWithoutFilter(c, cmd->subcommands_dict, numcmds); + if (cmd->subcommands_set) { + commandListWithoutFilter(c, cmd->subcommands_set, numcmds); } } - dictReleaseIterator(di); + hashsetResetIterator(&iter); } /* COMMAND LIST [FILTERBY (MODULE |ACLCAT |PATTERN )] */ @@ -5118,14 +5137,14 @@ 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)); + hashsetIterator iter; + struct serverCommand *cmd; + addReplyArrayLen(c, hashsetSize(server.commands)); + hashsetInitIterator(&iter, server.commands); + while (hashsetNext(&iter, (void **)&cmd)) { + addReplyCommandInfo(c, cmd); } - dictReleaseIterator(di); + hashsetResetIterator(&iter); } else { addReplyArrayLen(c, c->argc - 2); for (i = 2; i < c->argc; i++) { @@ -5139,16 +5158,15 @@ 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); + hashsetIterator iter; + struct serverCommand *cmd; + addReplyMapLen(c, hashsetSize(server.commands)); + hashsetInitIterator(&iter, server.commands); + while (hashsetNext(&iter, (void **)&cmd)) { addReplyBulkCBuffer(c, cmd->fullname, sdslen(cmd->fullname)); addReplyCommandDocs(c, cmd); } - dictReleaseIterator(di); + hashsetResetIterator(&iter); } else { /* Reply with an array of the requested commands (if we find them) */ int numcmds = 0; @@ -5268,14 +5286,12 @@ 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) { +sds genValkeyInfoStringCommandStats(sds info, hashset *commands) { struct serverCommand *c; - dictEntry *de; - dictIterator *di; - di = dictGetSafeIterator(commands); - while ((de = dictNext(di)) != NULL) { + hashsetIterator iter; + hashsetInitSafeIterator(&iter, commands); + while (hashsetNext(&iter, (void **)&c)) { 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" @@ -5285,11 +5301,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_set) { + info = genValkeyInfoStringCommandStats(info, c->subcommands_set); } } - dictReleaseIterator(di); + hashsetResetIterator(&iter); return info; } @@ -5306,24 +5322,22 @@ sds genValkeyInfoStringACLStats(sds info) { return info; } -sds genValkeyInfoStringLatencyStats(sds info, dict *commands) { +sds genValkeyInfoStringLatencyStats(sds info, hashset *commands) { struct serverCommand *c; - dictEntry *de; - dictIterator *di; - di = dictGetSafeIterator(commands); - while ((de = dictNext(di)) != NULL) { + hashsetIterator iter; + hashsetInitSafeIterator(&iter, commands); + while (hashsetNext(&iter, (void **)&c)) { 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_set) { + info = genValkeyInfoStringLatencyStats(info, c->subcommands_set); } } - dictReleaseIterator(di); + hashsetResetIterator(&iter); return info; } diff --git a/src/server.h b/src/server.h index 4fad8d2508..cd0eca3a51 100644 --- a/src/server.h +++ b/src/server.h @@ -67,6 +67,7 @@ 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 "hashset.h" /* Hash set */ #include "kvstore.h" /* Slot-based hash table */ #include "adlist.h" /* Linked lists */ #include "zmalloc.h" /* total memory usage aware version of malloc/free */ @@ -1658,8 +1659,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. */ + hashset *commands; /* Command table */ + hashset *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. */ @@ -2539,7 +2540,7 @@ struct serverCommand { * 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 + hashset *subcommands_set; /* A set that holds the subcommands, 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) */ @@ -3268,9 +3269,9 @@ int changeListener(connListener *listener); void closeListener(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(hashset *commands, sds s); struct serverCommand *lookupCommandBySds(sds s); -struct serverCommand *lookupCommandByCStringLogic(dict *commands, const char *s); +struct serverCommand *lookupCommandByCStringLogic(hashset *commands, const char *s); struct serverCommand *lookupCommandByCString(const char *s); struct serverCommand *lookupCommandOrOriginal(robj **argv, int argc); int commandCheckExistence(client *c, sds *err); @@ -3304,7 +3305,7 @@ void serverLogRawFromHandler(int level, const char *msg); void usage(void); void updateDictResizePolicy(void); void populateCommandTable(void); -void resetCommandTableStats(dict *commands); +void resetCommandTableStats(hashset *commands); void resetErrorTableStats(void); void adjustOpenFilesLimit(void); void incrementErrorCount(const char *fullerr, size_t namelen); @@ -4001,7 +4002,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);