summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOran Agra <oran@redislabs.com>2022-04-26 20:29:20 +0300
committerGitHub <noreply@github.com>2022-04-26 20:29:20 +0300
commit81926254586f64fc6a9b393bf9afb0d2eacc5234 (patch)
tree51e28f518fc8f6219fd82395f76df3266b5ed3d1
parent46ec6ad98e6d118bd98a447dcd2307e80bb0fcd6 (diff)
downloadredis-81926254586f64fc6a9b393bf9afb0d2eacc5234.tar.gz
Add module API flag for using enum configs as bit flags (#10643)
Enables registration of an enum config that'll let the user pass multiple keywords that will be combined with `|` as flags into the integer config value. ``` const char *enum_vals[] = {"none", "one", "two", "three"}; const int int_vals[] = {0, 1, 2, 4}; if (RedisModule_RegisterEnumConfig(ctx, "flags", 3, REDISMODULE_CONFIG_DEFAULT | REDISMODULE_CONFIG_BITFLAGS, enum_vals, int_vals, 4, getFlagsConfigCommand, setFlagsConfigCommand, NULL, NULL) == REDISMODULE_ERR) { return REDISMODULE_ERR; } ``` doing: `config set moduleconfigs.flags "two three"` will result in 6 being passed to`setFlagsConfigCommand`.
-rw-r--r--src/config.c7
-rw-r--r--src/module.c14
-rw-r--r--src/redismodule.h1
-rw-r--r--tests/modules/moduleconfigs.c24
-rw-r--r--tests/unit/moduleapi/moduleconfigs.tcl12
5 files changed, 51 insertions, 7 deletions
diff --git a/src/config.c b/src/config.c
index 805aececb..0d435fecb 100644
--- a/src/config.c
+++ b/src/config.c
@@ -547,12 +547,15 @@ void loadServerConfigFromString(char *config) {
} else if (!strcasecmp(argv[0],"loadmodule") && argc >= 2) {
queueLoadModule(argv[1],&argv[2],argc-2);
} else if (strchr(argv[0], '.')) {
- if (argc != 2) {
+ if (argc < 2) {
err = "Module config specified without value";
goto loaderr;
}
sds name = sdsdup(argv[0]);
- if (!dictReplace(server.module_configs_queue, name, sdsdup(argv[1]))) sdsfree(name);
+ sds val = sdsdup(argv[1]);
+ for (int i = 2; i < argc; i++)
+ val = sdscatfmt(val, " %S", argv[i]);
+ if (!dictReplace(server.module_configs_queue, name, val)) sdsfree(name);
} else if (!strcasecmp(argv[0],"sentinel")) {
/* argc == 1 is handled by main() as we need to enter the sentinel
* mode ASAP. */
diff --git a/src/module.c b/src/module.c
index a1e104c0d..99d2adcd4 100644
--- a/src/module.c
+++ b/src/module.c
@@ -11325,6 +11325,7 @@ int moduleVerifyConfigFlags(unsigned int flags, configType type) {
| REDISMODULE_CONFIG_HIDDEN
| REDISMODULE_CONFIG_PROTECTED
| REDISMODULE_CONFIG_DENY_LOADING
+ | REDISMODULE_CONFIG_BITFLAGS
| REDISMODULE_CONFIG_MEMORY))) {
serverLogRaw(LL_WARNING, "Invalid flag(s) for configuration");
return REDISMODULE_ERR;
@@ -11333,6 +11334,10 @@ int moduleVerifyConfigFlags(unsigned int flags, configType type) {
serverLogRaw(LL_WARNING, "Numeric flag provided for non-numeric configuration.");
return REDISMODULE_ERR;
}
+ if (type != ENUM_CONFIG && flags & REDISMODULE_CONFIG_BITFLAGS) {
+ serverLogRaw(LL_WARNING, "Enum flag provided for non-enum configuration.");
+ return REDISMODULE_ERR;
+ }
return REDISMODULE_OK;
}
@@ -11534,6 +11539,12 @@ unsigned int maskModuleNumericConfigFlags(unsigned int flags) {
return new_flags;
}
+unsigned int maskModuleEnumConfigFlags(unsigned int flags) {
+ unsigned int new_flags = 0;
+ if (flags & REDISMODULE_CONFIG_BITFLAGS) new_flags |= MULTI_ARG_CONFIG;
+ return new_flags;
+}
+
/* Create a string config that Redis users can interact with via the Redis config file,
* `CONFIG SET`, `CONFIG GET`, and `CONFIG REWRITE` commands.
*
@@ -11573,6 +11584,7 @@ unsigned int maskModuleNumericConfigFlags(unsigned int flags) {
* * REDISMODULE_CONFIG_PROTECTED: This config will be only be modifiable based off the value of enable-protected-configs.
* * REDISMODULE_CONFIG_DENY_LOADING: This config is not modifiable while the server is loading data.
* * REDISMODULE_CONFIG_MEMORY: For numeric configs, this config will convert data unit notations into their byte equivalent.
+ * * REDISMODULE_CONFIG_BITFLAGS: For enum configs, this config will allow multiple entries to be combined as bit flags.
*
* Default values are used on startup to set the value if it is not provided via the config file
* or command line. Default values are also used to compare to on a config rewrite.
@@ -11688,7 +11700,7 @@ int RM_RegisterEnumConfig(RedisModuleCtx *ctx, const char *name, int default_val
enum_vals[num_enum_vals].name = NULL;
enum_vals[num_enum_vals].val = 0;
listAddNodeTail(module->module_configs, new_config);
- flags = maskModuleConfigFlags(flags);
+ flags = maskModuleConfigFlags(flags) | maskModuleEnumConfigFlags(flags);
addModuleEnumConfig(module->name, name, flags, new_config, default_val, enum_vals);
return REDISMODULE_OK;
}
diff --git a/src/redismodule.h b/src/redismodule.h
index e72f5f7bc..cd389dd00 100644
--- a/src/redismodule.h
+++ b/src/redismodule.h
@@ -88,6 +88,7 @@
#define REDISMODULE_CONFIG_DENY_LOADING (1ULL<<6) /* This config is forbidden during loading. */
#define REDISMODULE_CONFIG_MEMORY (1ULL<<7) /* Indicates if this value can be set as a memory value */
+#define REDISMODULE_CONFIG_BITFLAGS (1ULL<<8) /* Indicates if this value can be set as a multiple enum values */
/* StreamID type. */
typedef struct RedisModuleStreamID {
diff --git a/tests/modules/moduleconfigs.c b/tests/modules/moduleconfigs.c
index af30bda34..0a6380461 100644
--- a/tests/modules/moduleconfigs.c
+++ b/tests/modules/moduleconfigs.c
@@ -6,6 +6,7 @@ long long longval;
long long memval;
RedisModuleString *strval = NULL;
int enumval;
+int flagsval;
/* Series of get and set callbacks for each type of config, these rely on the privdata ptr
* to point to the config, and they register the configs as such. Note that one could also just
@@ -68,6 +69,20 @@ int setEnumConfigCommand(const char *name, int val, void *privdata, RedisModuleS
return REDISMODULE_OK;
}
+int getFlagsConfigCommand(const char *name, void *privdata) {
+ REDISMODULE_NOT_USED(name);
+ REDISMODULE_NOT_USED(privdata);
+ return flagsval;
+}
+
+int setFlagsConfigCommand(const char *name, int val, void *privdata, RedisModuleString **err) {
+ REDISMODULE_NOT_USED(name);
+ REDISMODULE_NOT_USED(err);
+ REDISMODULE_NOT_USED(privdata);
+ flagsval = val;
+ return REDISMODULE_OK;
+}
+
int boolApplyFunc(RedisModuleCtx *ctx, void *privdata, RedisModuleString **err) {
REDISMODULE_NOT_USED(ctx);
REDISMODULE_NOT_USED(privdata);
@@ -106,10 +121,13 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
}
/* On the stack to make sure we're copying them. */
- const char *enum_vals[3] = {"one", "two", "three"};
- const int int_vals[3] = {0, 2, 4};
+ const char *enum_vals[] = {"none", "one", "two", "three"};
+ const int int_vals[] = {0, 1, 2, 4};
- if (RedisModule_RegisterEnumConfig(ctx, "enum", 0, REDISMODULE_CONFIG_DEFAULT, enum_vals, int_vals, 3, getEnumConfigCommand, setEnumConfigCommand, NULL, NULL) == REDISMODULE_ERR) {
+ if (RedisModule_RegisterEnumConfig(ctx, "enum", 1, REDISMODULE_CONFIG_DEFAULT, enum_vals, int_vals, 4, getEnumConfigCommand, setEnumConfigCommand, NULL, NULL) == REDISMODULE_ERR) {
+ return REDISMODULE_ERR;
+ }
+ if (RedisModule_RegisterEnumConfig(ctx, "flags", 3, REDISMODULE_CONFIG_DEFAULT | REDISMODULE_CONFIG_BITFLAGS, enum_vals, int_vals, 4, getFlagsConfigCommand, setFlagsConfigCommand, NULL, NULL) == REDISMODULE_ERR) {
return REDISMODULE_ERR;
}
/* Memory config here. */
diff --git a/tests/unit/moduleapi/moduleconfigs.tcl b/tests/unit/moduleapi/moduleconfigs.tcl
index 29682d2e2..f731d1bd9 100644
--- a/tests/unit/moduleapi/moduleconfigs.tcl
+++ b/tests/unit/moduleapi/moduleconfigs.tcl
@@ -11,6 +11,7 @@ start_server {tags {"modules"}} {
assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 1024"
assert_equal [r config get moduleconfigs.string] "moduleconfigs.string {secret password}"
assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum one"
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {one two}"
assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1"
}
@@ -29,6 +30,10 @@ start_server {tags {"modules"}} {
assert_equal [r config get moduleconfigs.string] "moduleconfigs.string {super \0secret password}"
r config set moduleconfigs.enum two
assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum two"
+ r config set moduleconfigs.flags two
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags two"
+ r config set moduleconfigs.flags "two three"
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {two three}"
r config set moduleconfigs.numeric -2
assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -2"
}
@@ -67,6 +72,7 @@ start_server {tags {"modules"}} {
assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 1024"
assert_equal [r config get moduleconfigs.string] "moduleconfigs.string {secret password}"
assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum one"
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {one two}"
assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1"
r module unload moduleconfigs
}
@@ -80,6 +86,7 @@ start_server {tags {"modules"}} {
assert_equal [r config get moduleconfigs.string] "moduleconfigs.string tclortickle"
# Configs that were not changed should still be their module specified value
assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum one"
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {one two}"
assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1"
}
@@ -140,6 +147,7 @@ start_server {tags {"modules"}} {
r config set moduleconfigs.mutable_bool yes
r config set moduleconfigs.memory_numeric 750
r config set moduleconfigs.enum two
+ r config set moduleconfigs.flags "two three"
r config rewrite
restart_server 0 true false
# Ensure configs we rewrote are present and that the conf file is readable
@@ -147,6 +155,7 @@ start_server {tags {"modules"}} {
assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 750"
assert_equal [r config get moduleconfigs.string] "moduleconfigs.string {super \0secret password}"
assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum two"
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {two three}"
assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1"
r module unload moduleconfigs
}
@@ -221,11 +230,12 @@ start_server {tags {"modules"}} {
set stdout [dict get $noload stdout]
assert_equal [count_message_lines $stdout "Module Configurations were not set, likely a missing LoadConfigs call. Unloading the module."] 1
- start_server [list overrides [list loadmodule "$testmodule" moduleconfigs.string "bootedup" moduleconfigs.enum two]] {
+ start_server [list overrides [list loadmodule "$testmodule" moduleconfigs.string "bootedup" moduleconfigs.enum two moduleconfigs.flags "two three"]] {
assert_equal [r config get moduleconfigs.string] "moduleconfigs.string bootedup"
assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool yes"
assert_equal [r config get moduleconfigs.immutable_bool] "moduleconfigs.immutable_bool no"
assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum two"
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {two three}"
assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1"
assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 1024"
}