Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API UpdateRuntimeArgs to allow the module arguments during runtime and save the new arguments value into the conf file #1041

Open
wants to merge 9 commits into
base: unstable
Choose a base branch
from

Conversation

hwware
Copy link
Member

@hwware hwware commented Sep 17, 2024

Before Redis OSS 7, if we load a module with some arguments during runtime,
and run the command "config rewrite", the module information will not be saved into the
config file.

Since Redis OSS 7 and Valkey 7.2, if we load a module with some arguments during runtime,
the module information (path, arguments number, and arguments value) can be saved into the config file after config rewrite command is called.
Thus, the module will be loaded automatically when the server startup next time.

Following is one example:

bind 172.25.0.58
port 7000
protected-mode no
enable-module-command yes

Generated by CONFIG REWRITE
latency-tracking-info-percentiles 50 99 99.9
dir "/home/ubuntu/valkey"
save 3600 1 300 100 60 10000
user default on nopass sanitize-payload ~* &* +https://github.com/ALL
loadmodule tests/modules/datatype.so 10 20

However, there is one problem.
If developers write a module, and update the running arguments by someway, the updated arguments can not be saved into the config file even "config rewrite" is called.
The reason comes from the following function
rewriteConfigLoadmoduleOption (src/config.c)

void rewriteConfigLoadmoduleOption(struct rewriteConfigState *state) {
..........
struct ValkeyModule *module = dictGetVal(de);
line = sdsnew("loadmodule ");
line = sdscatsds(line, module->loadmod->path);
for (int i = 0; i < module->loadmod->argc; i++) {
line = sdscatlen(line, " ", 1);
line = sdscatsds(line, module->loadmod->argv[i]->ptr);
}
rewriteConfigRewriteLine(state, "loadmodule", line, 1);
.......
}

The function only save the initial arguments information (module->loadmod) into the configfile.

After core members discuss, ref #1177

We decide add the following API to implement this feature:

Original proposal:

int VM_UpdateRunTimeArgs(ValkeyModuleCtx *ctx, int index, char *value);

Updated proposal:

ValkeyModuleString **values VM_GetRuntimeArgs(ValkeyModuleCtx *ctx);
**int VM_UpdateRuntimeArgs(ValkeyModuleCtx *ctx, int argc, ValkeyModuleString **values);

Why we do not recommend the following way:

MODULE UNLOAD
Update module args in the conf file
MODULE LOAD

I think there are the following disadvantages:

  1. Some modules can not be unloaded. Such as the example module datatype.so, which is tests/modules/datatype.so
  2. it is not atomic operation for MODULE UNLOAD + MODULE LOAD
  3. sometimes, if we just run the module unload, the client business could be interrupted

@hwware hwware force-pushed the update-module-parameter-runtime branch from c52d35c to c3341b1 Compare September 17, 2024 17:45
Copy link

codecov bot commented Sep 17, 2024

Codecov Report

Attention: Patch coverage is 8.33333% with 11 lines in your changes missing coverage. Please review.

Project coverage is 70.69%. Comparing base (3c32ee1) to head (a117ce9).
Report is 9 commits behind head on unstable.

Files with missing lines Patch % Lines
src/module.c 8.33% 11 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           unstable    #1041      +/-   ##
============================================
+ Coverage     70.65%   70.69%   +0.03%     
============================================
  Files           114      114              
  Lines         63158    63159       +1     
============================================
+ Hits          44624    44649      +25     
+ Misses        18534    18510      -24     
Files with missing lines Coverage Δ
src/module.c 9.66% <8.33%> (-0.01%) ⬇️

... and 18 files with indirect coverage changes

@hwware hwware force-pushed the update-module-parameter-runtime branch 2 times, most recently from 3bb1f12 to 9884508 Compare September 24, 2024 00:37
@hwware hwware force-pushed the update-module-parameter-runtime branch from 9884508 to 8a9d80e Compare September 26, 2024 00:37
@zuiderkwast
Copy link
Contributor

Looks simple, but what's the use case? Why does a module need to update args in runtime?

A module can already have config that is updated using CONFIG SET, right?

@hwware hwware force-pushed the update-module-parameter-runtime branch 2 times, most recently from eb9712a to fbc66dd Compare October 14, 2024 00:43
@hwware hwware force-pushed the update-module-parameter-runtime branch from 5038d52 to ed24ce1 Compare October 14, 2024 13:21
@zuiderkwast zuiderkwast added the major-decision-pending Major decision pending by TSC team label Oct 14, 2024
@hwware hwware changed the title Add Module set-argument command for updating module parameter during rumtime Add Module set-argument command for updating module parameter and use them by GetRunTimeArgs during rumtime Oct 15, 2024
@hwware
Copy link
Member Author

hwware commented Oct 15, 2024

@zuiderkwast I update the top description for this PR, i think this is a good way to access the updated module arguments. Pls take a look when you have time, Thanks a lot

@zuiderkwast
Copy link
Contributor

zuiderkwast commented Oct 15, 2024

You added a module API to access the argv. Interesting.

But, I am not convinced. :)

In a program written in C, the arguments are passed to main as int main(int argc, char **argv). It's not possible to change these args later, from outside the program, while the program is running.

The argv for MODULE LOAD is the same. If you set the arguments with MODULE SET-ARGUMENTS later, the module was still loaded with the old arguments and most modules (or new modules like valkey-bloom) will not notice the arguments have changed. But there will be a problem. In the log, it looks like the module is using the new arguments, even if it is using the old arguments.

MODULE UNLOAD + MODULE LOAD doesn't have this problem.

@hwware hwware force-pushed the update-module-parameter-runtime branch from ed24ce1 to 50aa816 Compare October 16, 2024 01:03
@hwware
Copy link
Member Author

hwware commented Oct 16, 2024

You added a module API to access the argv. Interesting.

But, I am not convinced. :)

In a program written in C, the arguments are passed to main as int main(int argc, char **argv). It's not possible to change these args later, from outside the program, while the program is running.

The argv for MODULE LOAD is the same. If you set the arguments with MODULE SET-ARGUMENTS later, the module was still loaded with the old arguments and most modules (or new modules like valkey-bloom) will not notice the arguments have changed. But there will be a problem. In the log, it looks like the module is using the new arguments, even if it is using the old arguments.

MODULE UNLOAD + MODULE LOAD doesn't have this problem.

Thanks for your comment, but some cons for operation MODULE UNLOAD + MODULE LOAD

  1. some modules maybe not unloaded. Such as the example module datatype.so, which is tests/modules/datatype.so
  2. it is not atomic operation for MODULE UNLOAD + MODULE LOAD
  3. sometimes, if we just run the module unload, the client business could be interrupted
  4. through the way MODULE UNLOAD + MODULE LOAD to update module runtime args, the management plan and data plan software need to be updated as well.

I would like to create an issue to let community to discuss this problem. Thanks

@hwware hwware force-pushed the update-module-parameter-runtime branch from 50aa816 to e313f86 Compare October 22, 2024 03:16
@hwware hwware force-pushed the update-module-parameter-runtime branch 2 times, most recently from 0caa3bd to 69c5083 Compare October 31, 2024 02:25
@hwware hwware changed the title Add Module set-argument command for updating module parameter and use them by GetRunTimeArgs during rumtime Update the module args during runtime and save the new args value into the conf file Oct 31, 2024
@hwware hwware force-pushed the update-module-parameter-runtime branch from 69c5083 to b4d698d Compare October 31, 2024 06:49
@hwware hwware changed the title Update the module args during runtime and save the new args value into the conf file Add API UpdateRuntimeArgs to allow the module arguments during runtime and save the new arguments value into the conf file Oct 31, 2024
@hwware
Copy link
Member Author

hwware commented Oct 31, 2024

@madolson As we discussed, I desgin the API as int VM_UpdateRuntimeArgs(ValkeyModuleCtx *ctx, int argc, ValkeyModuleString **argv), please take a look, Thanks

src/module.c Outdated Show resolved Hide resolved
@hwware hwware force-pushed the update-module-parameter-runtime branch from b4d698d to c35a618 Compare November 1, 2024 00:57
Copy link
Contributor

@zuiderkwast zuiderkwast left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Only documentation comment missing.

What about VM_GetRuntimeArgs, is it not needed?

src/module.c Outdated
@@ -3042,6 +3042,23 @@ client *moduleGetReplyClient(ValkeyModuleCtx *ctx) {
}
}

int VM_UpdateRuntimeArgs(ValkeyModuleCtx *ctx, int argc, ValkeyModuleString **argv) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment. It is used for generated documentation. Without a comment, the function will not be included in the API documentation. The comment needs to use correct markdown syntax.

You can try the ruby script to generate the documentation markdown: utils/generate-module-api-doc.rb. (Later, we run the script and save the output in the valkey-doc repo in the file topics/module-api-ref.md.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed, add comments already

tests/unit/moduleapi/moduleconfigs.tcl Outdated Show resolved Hide resolved
@hwware hwware force-pushed the update-module-parameter-runtime branch from fbc6ca1 to 67e9459 Compare November 3, 2024 13:47
src/module.c Outdated
Comment on lines 2257 to 2260
/* ValkeyModule_UpdateRuntimeArgs can be used to update the values of module->loadmod
* so that the updated values can be saved into conf file once 'config rewrite' command
* is called
* One example can be found in file modules/moduleparameter.c
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good, but I have some small comments:

module->loadmod is something internal in Valkey. Module authors are not aware of this. Let's assume module authors are know about the API documentation and valkeymodule.h.

Usually we write commands in uppercase, like CONFIG REWRITE.

Dot is missing after "is called".

Is the example in src/modules/ or tests/modules/ ? We have both, so it's better to be specific. Also it can be good mention that that it is in the Valkey source code, because the documentation is published in other places and module authors are maybe not even looking at valkey source code.

src/module.c Outdated Show resolved Hide resolved
@hwware hwware force-pushed the update-module-parameter-runtime branch from 2d51ed9 to e260229 Compare November 5, 2024 02:36
Copy link
Contributor

@zuiderkwast zuiderkwast left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for many small reviews. I was going to click Approve, but then I read the test case. more carefully and found some strange function and command names. :)

Comment on lines 16 to 17
if (ValkeyModule_Init(ctx,"myhello",1,VALKEYMODULE_APIVER_1)
== VALKEYMODULE_ERR) return VALKEYMODULE_ERR;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"myhello"?

Maybe "moduleparameter" is more descriptive?

Comment on lines 20 to 21
if (ValkeyModule_CreateCommand(ctx,"hello.hi",
GET_HELLO,"fast",0,0,0) == VALKEYMODULE_ERR)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Command name "hello.hi" and function name GET_HELLO are not perfect names. :)

Something like "updateargs", updateArgsCommand?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your comments, it is updated

Comment on lines +8 to +10
ValkeyModule_UpdateRuntimeArgs(ctx, argv, argc);
return ValkeyModule_ReplyWithSimpleString(ctx, "Module runtime args test");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ValkeyModule_UpdateRuntimeArgs(ctx, argv, argc);
return ValkeyModule_ReplyWithSimpleString(ctx, "Module runtime args test");
ValkeyModule_UpdateRuntimeArgs(ctx, argv, argc);
return ValkeyModule_ReplyWithSimpleString(ctx, "Module runtime args test");

I guess we should probably apply the formatter on these files as well.

src/module.c Outdated
* The function parameter 'argc' indicates the number of updated arguments, and 'argv'
* represents the values of the updated arguments.
* Once 'CONFIG REWRITE' command is called, the updated argument values can be saved into conf file.
* One example can be found in file tests/modules/moduleparameter.c.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't normally reference the test modules since these are rendered here: https://valkey.io/topics/modules-api-ref/, which doesn't clearly map to the test files anymore. I'm not sure it's really needed to show an example.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like no specific test file is mentioned in reference, I will remove it.

Comment on lines +2268 to +2269
if (!ctx->module->onload) {
return VALKEYMODULE_ERR;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this only allowed at startup? I thought the whole point was they were able to update these at any point in time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I'm surprised the test case passes.

Copy link
Member Author

@hwware hwware Nov 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition "!ctx->module->onload" is checked here is not like what you thought it can be only allowed to update at startup, the goal is to check if ctx->module->onload is NULL to prevent NULL pointer exception. This API can be called at any time during runtime.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it means that onload is ongoing. If it doesn't, then there is something wrong.

I tried to follow the function calls that happen when a module is loaded. This is what I could find:

  • MODULE LOAD -> moduleCommand -> moduleLoad.
  • moduleLoad loads the module file using dlopen.
  • moduleLoad finds the ValkeyModule_OnLoad function in the module using dlsym.
  • moduleLoad calls the ValkeyModule_OnLoad in the module.
  • The module's ValkeyModule_OnLoad function calls ValkeyModule_Init.
  • ValkeyModule_Init (implemented in valkeymodule.h) calls ValkeyModule_SetModuleAttribs.
  • VM_SetModuleAttribs creates the struct ctx->module and sets ctx->module->onload = 1.
  • The ValkeyModule_OnLoad function calls other functions, such as ValkeyModule_CreateCommand
  • The ValkeyModule_OnLoad function returns.
  • moduleLoad logs serverLog(LL_NOTICE, "Module '%s' loaded from %s", ctx.module->name, path);
  • moduleLoad sets ctx.module->onload = 0;
  • This ctx is freed, but the module object is added to a dict.

There is no other place that onload is set. That means onload = 1 only when ValkeyModule_OnLoad is running.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The onload field in ValkeyModule is not a pointer. It is an int.

int onload;                           /* Flag to identify if the call is being made from Onload (0 or 1) */

@@ -357,6 +357,7 @@
#define RedisModule_SetModuleAttribs ValkeyModule_SetModuleAttribs
#define RedisModule_IsModuleNameBusy ValkeyModule_IsModuleNameBusy
#define RedisModule_WrongArity ValkeyModule_WrongArity
#define RedisModule_UpdateRuntimeArgs ValkeyModule_UpdateRuntimeArgs
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Redis module API is just for backward compatibilty. We don't add functions to that API.

Suggested change
#define RedisModule_UpdateRuntimeArgs ValkeyModule_UpdateRuntimeArgs

Signed-off-by: hwware <wen.hui.ware@gmail.com>
Signed-off-by: hwware <wen.hui.ware@gmail.com>
Signed-off-by: hwware <wen.hui.ware@gmail.com>
Signed-off-by: hwware <wen.hui.ware@gmail.com>
Signed-off-by: hwware <wen.hui.ware@gmail.com>
Signed-off-by: hwware <wen.hui.ware@gmail.com>
Signed-off-by: hwware <wen.hui.ware@gmail.com>
Signed-off-by: hwware <wen.hui.ware@gmail.com>
Signed-off-by: hwware <wen.hui.ware@gmail.com>
@hwware hwware force-pushed the update-module-parameter-runtime branch from d62c9cd to a117ce9 Compare November 8, 2024 09:05

int test_module_update_parameter(ValkeyModuleCtx *ctx,
ValkeyModuleString **argv, int argc) {
ValkeyModule_UpdateRuntimeArgs(ctx, argv, argc);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check that ValkeyModule_UpdateRuntimeArgs returns VALKEYMODULE_OK here.

assert_not_equal [lsearch $modulename moduleparameter] -1
string match "10 20 30" [lmap x [r module list] {dict get $x args}]
r testmoduleparameter.update.parameter 40 50 60 70
string match "40 50 60 70" [lmap x [r module list] {dict get $x args}]
Copy link
Contributor

@zuiderkwast zuiderkwast Nov 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not an assert. It will silently fail. It just returns 0 and the return value is ignored here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
major-decision-pending Major decision pending by TSC team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[NEW]Update the module args during runtime and save the new args value into the conf file
4 participants