-
Notifications
You must be signed in to change notification settings - Fork 24.4k
Redis Function #9780
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
Redis Function #9780
Conversation
|
@redis/core-team please review / approve and state what's missing or needs a change. |
|
@MeirShpilraien I noticed the functionsCtxGetCurrent gets a global value. Does it make sense that it could live within the redisDb struct? Same style as slots_to_keys, that was moved there recently. |
|
@eduardobr the problem is that functions are global (not per db, in a multi-db server). Note, i did review this code before being posted publicly, and did attempt to follow the cluster slots footsteps as much as i could. |
yossigo
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but did not do a full code review of the latest version.
itamarhaber
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving the feature and API, did not do a code review.
madolson
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Sorry for random comments scattered, I was just making notes on stuff I saw. I mostly was trying to understand the interface. It looked good, so I trust the functionality is implemented as expected)
I want to better understand why we think the right approach here is storing functions in the RDB as opposed to storing them in the conf file or a separate file. I view functions more analogous to ACLs than keyspace data, that they are properties of the cluster and not necessarily specific to the RDB data. This is basically completely broken for workloads without persistence, like caching, which seems relevant. I would expect having a well defined mechanism outside of the RDB to exist.
Also are we fixing this? #8478 for functions?
|
@madolson thanks for the review. Fixed most of the comments (pushed a new commit) and I added comments where I needed more input about how to proceed. Regarding pre-load of functions (not from persistence). This is listed as first task to handle on following PR's. I am working on the design and will share in a few days. Once you (the core team) will approve the design I will work on the implementation. Regarding this #8478, I have another design coming to allow better code sharing and I hope to address it (or at least suggest a possible solution). Will share soon. |
|
@MeirShpilraien My point is that I'm not convinced RDB is the right place to be storing functions, or at least we should take a deeper look into the RDB storage to think about how it interacts with other features. One example is cluster mode, when you want to split a cluster, you add N new nodes with the same configs of the other nodes and then migrate the keyspace data off for some slices. This won't include functions until we add them to configs, but then you run into the issue where they need to only be loaded on primaries and replicated (or perhaps we allow primaries and replicas to diverge?). That's why I want to understand why it's not being built more analogous to the config file or to ACLs. The only real benefit here is that storing it in the RDB is actually really convenient (Since you can just restore the RDB to another node). But it would also be convenient for ACLs, so maybe the right answer is we should be storing them both in the RDB. (Also, I didn't actually review the code but I assume we replicate by the effects of the script and not the script itself. If that isn't the case, then having replica and primary agree on the state of a function matter more) I suppose I would want an answer to this before I would signoff on it. |
|
@madolson the entire point of functions is that they are replicated and persisted so you will have them on fail-overs for example (this is also how we defined it in the issue #8693). I do agree though that we need a way to allow users to also load them from config in case the user runs without persistence for example. Hope to publish my design for that today/tomorrow and we will be able to discuss it.
Yes, on functions we replicate only by effect. |
|
@MeirShpilraien Agree about having them available on failover, if they were stored in a config file they would also be available though. |
The functionality was moved to script_lua.c under callFunction function. Its purpose is to call the Lua function already located on the top of the Lua stack. Used the new function on eval.c to invoke Lua code. The function will also be used to invoke Lua code on the Lua engine.
726a47f to
6b6f375
Compare
|
Squashed all reviews commits to their relevant location, kept only the original 5 commits. |
|
triggered full CI https://github.com/redis/redis/actions/runs/1529820590 |
Redis function unit is located inside functions.c and contains Redis Function implementation: 1. FUNCTION commands: * FUNCTION CREATE * FCALL * FCALL_RO * FUNCTION DELETE * FUNCTION KILL * FUNCTION INFO 2. Register engine In addition, this commit introduce the first engine that uses the Redis Function capabilities, the Lua engine.
cdffd0b to
cbd4631
Compare
madolson
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Talked with @Oran over slack, and it seems like we're definitely going to make sure there is another strategy for making functions loadable at startup/cluster mode. So we can follow up on that issue.
|
Loading Functions on cluster and/or startup time is discussed here: #9899 |
|
Code sharing on functions is discussed here: #9906 |
…ic execution logic (#9812) # Background The main goal of this PR is to remove relevant logics on Lua script verbatim replication, only keeping effects replication logic, which has been set as default since Redis 5.0. As a result, Lua in Redis 7.0 would be acting the same as Redis 6.0 with default configuration from users' point of view. There are lots of reasons to remove verbatim replication. Antirez has listed some of the benefits in Issue #5292: >1. No longer need to explain to users side effects into scripts. They can do whatever they want. >2. No need for a cache about scripts that we sent or not to the slaves. >3. No need to sort the output of certain commands inside scripts (SMEMBERS and others): this both simplifies and gains speed. >4. No need to store scripts inside the RDB file in order to startup correctly. >5. No problems about evicting keys during the script execution. When looking back at Redis 5.0, antirez and core team decided to set the config `lua-replicate-commands yes` by default instead of removing verbatim replication directly, in case some bad situations happened. 3 years later now before Redis 7.0, it's time to remove it formally. # Changes - configuration for lua-replicate-commands removed - created config file stub for backward compatibility - Replication script cache removed - this is useless under script effects replication - relevant statistics also removed - script persistence in RDB files is also removed - Propagation of SCRIPT LOAD and SCRIPT FLUSH to replica / AOF removed - Deterministic execution logic in scripts removed (i.e. don't run write commands after random ones, and sorting output of commands with random order) - the flags indicating which commands have non-deterministic results are kept as hints to clients. - `redis.replicate_commands()` & `redis.set_repl()` changed - now `redis.replicate_commands()` does nothing and return an 1 - ...and then `redis.set_repl()` can be issued before `redis.replicate_commands()` now - Relevant TCL cases adjusted - DEBUG lua-always-replicate-commands removed # Other changes - Fix a recent bug comparing CLIENT_ID_AOF to original_client->flags instead of id. (introduced in #9780) Co-authored-by: Oran Agra <oran@redislabs.com>
This is an overlook in redis#9780
We now no longer propagate scripts (started from 7.0), so this is a very rare issue that in nearly-dead-code. This is an overlook in #9780
Info fields from the rearrangement of scripts when functions were introduced. see redis/redis#9780 --------- Co-authored-by: Oran Agra <oran@redislabs.com>
Redis Function
This PR added the Redis Functions capabilities that were suggested on #8693. The PR also introduce a big refactoring to the current Lua implementation (i.e
scripting.c). The main purpose of the refactoring is to have better code sharing between the Lua implementation that exists today on Redis (scripting.c) and the new Lua engine that is introduced on this PR. The refactoring includes code movements and file name changes as well as some logic changes that need to be carefully reviewed. To make the review easier, the PR was split into multiple commits. Each commit is deeply described later on but the main concept is that some commits are just moving code around without making any logical changes, those commits are less likely to cause any issues or regressions and can be reviewed fast. Other commits, which perform code and logic changes, need to be reviewed carefully, but those commits were created after the code movements so it's pretty easy to see what was changed. To sum up, it is highly recommended to review this PR commit by commit as it will be easier to see the changes, it is also recommended to read each commit description (written below) to understand what was changed on the commit and whether or not it's just a huge code movement or a logic changes.Terminology
Currently, the terminology in Redis is not clearly defined. Scripts refer to Lua scripts and eval also refers only to Lua. Introducing Redis Function requires redefining those terms to be able to clearly understand what is been discussed on each context.
evalorevalsha)Refactoring New Structure
Today, the entire scripting logic is located on
scripting.c. This logic can be split into 3 main groups:evalorevalshacommand (this part includes finding the relevant script, preparing the arguments, ..)Those 3 groups are tightly coupled on
scripting.c. Redis Functions also need to use those groups logics, for example, to interact back with Redis or to execute Lua code. The refactoring attempts to split those 3 groups and define APIs so that we can reuse the code both on legacy Lua scripts and Redis Functions.In order to do so we define the following units:
script.cto interact with Redis from within the Lua code.script_lua.cto execute the Lua code.FUNCTIONcommand,), usesfunctions_lua.cif the function it wants to invoke needs the Lua engine.scripting.ccontains the Lua legacy implementation and was refactored to usescript_lua.cto invoke the Lua code.Commits breakdown
Notice: Some small commits are omitted from this list as they are small and insignificant (for example build fixes)
First commit - code movements
This commit rename
scripting.c->eval.cand introduce the newscript_lua.cunit. The commit moves relevant code fromeval.c(scripting.c) toscript_lua.c, the purpose of moving the code is so that later we will be able to re-use the code on the Lua engine (function_lua.c). The commit only moves the code without modifying even a single line, so there is a very low risk of breaking anything and it also makes it much easier to see the changes on the following commits.Because the commit does not change the code (only moves it), it does not compile. But we do not care about it as the only purpose here is to make the review processes simpler.
Second commit - move legacy Lua variables into
eval.cToday, all Lua-related variables are located on the server struct. The commit attempt to identify those variable and take them out from the server struct, leaving only script related variables (variables that later need to be used also by engines)
The following variable where renamed and left on the server struct:
The following variables where moved to lctx under eval.c
This commit is in a low risk of introducing any issues and it is just moving variables around and not changing any logic.
Third commit - introducing script unit
This commit introduces the
script.cunit. Its purpose (as described above) is to provide an API for scripts to interact with Redis. Interaction includes mostly executing commands, but also other functionalities. The interaction is done using aScriptRunCtxobject that needs to be created by the user and initialized usingscriptPrepareForRun. A detailed list of functionalities expose by the unit:acl, cluster, read only run, ...)
The commit introduces the new unit and uses it on eval commands to interact with Redis.
Fourth commit - Moved functionality of invoke Lua code to
script_lua.cThis commit moves the logic of invoking the Lua code into
script_lua.cso later it can be used also by Lua engine (function_lua.c). The code is located oncallFunctionfunction and assumes the Lua function already located on the top of the Lua stack. This commit also changeeval.cto use the new functionality to invoke Lua code.Fith commit - Added Redis Functions unit (
functions.c) and Lua engine (function_lua.c)Added Redis Functions unit under
functions.c, included:In addition, this commit introduces the first engine that uses the Redis Functions capabilities, the Lua engine (
function_lua.c)API Changes
lua-time-limitconfiguration was renamed to
script-time-limit(keeplua-time-limitas alias for backward compatibility).Error log changes
When integrating with Redis from within a Lua script, the
Luaterm was removed from all the error messages and instead we write onlyscript. For example:Wrong number of args calling Redis command From Lua script->Wrong number of args calling Redis command From scriptinfo memorychanges:Before stating all the changes made to memory stats we will try to explain the reason behind them and what we want to see on those metrics:
A full breakdown of
info memorychanges:used_memory_luaandused_memory_lua_humanwas deprecated,used_memory_vm_evalhas the same meaning asused_memory_luaused_memory_scriptswas renamed toused_memory_scripts_evalused_memory_scriptsandused_memory_scripts_humanwere repurposed and now return the total memory used by functions and eval (not including vm memory, only code cache, and structs).used_memory_vm_functionwas added and represents the total memory used by functions vm'sused_memory_functionswas added and represents the total memory by functions (not including vm memory, only code cache, and structs)used_memory_vm_totalandused_memory_vm_total_humanwas added and represents the total memory used by vm's (functions and eval combined)functions.cachesfunctions.cachesfield was added tomemory stats, representing the memory used by engines that are not functions (this memory includes data structures like dictionaries, arrays, ...)New API
FUNCTION LOAD
Usage: FUNCTION LOAD
ENGINENAME[REPLACE][DESC <DESCRIPTION>]<CODE>ENGINE- The name of the engine to use to create the script.NAME- the name of the function that can be used later to call the function usingFUNCTION CALLcommand.REPLACE- if given, replace the given function with the existing function (if exists).DESCRIPTION- optional argument describing the function and what it doesCODE- function code.The command will return
OKif created successfully or error in the following cases:REPLACEwas not used.FCALL and FCALL_RO
Usage: FCALL/FCALL_RO
NAMENUM_KEYS key1 key2…arg1 arg2Call and execute the function specified by
NAME. The function will receive all arguments given afterNUM_KEYS. The return value from the function will be returned to the user as a result.NAME- Name of the function to run.The command will return an error in the following cases:
NAMEdoes not existThe
FCALL_ROis equivalent toEVAL_ROand allows only read-only commands to be invoked from the script.FUNCTION DELETE
Usage: FUNCTION DELETE
NAMEDelete a function identified by
NAME. ReturnOKon success or error on one of the following:FUNCTION INFO
Usage: FUNCTION INFO
NAME[WITHCODE]Return information about a function by function name:
FUNCTION LIST
Usage: FUNCTION LIST
Return general information about all the functions:
FUNCTION STATS
Usage: FUNCTION STATS
Return information about the current running function:
If no function is currently running, this section is just a RESP nil.
Additionally, return a list of all the available engines.
FUNCTION KILL
Usage:
FUNCTION KILLKill the currently executing function. The command will fail if the function already initiated a write command.
TODO
Community requests to handle on following PR's
Community requests handled on this PR's
FUNCTION RUNNING command, to check to current running function: Suggestion: Redis Functions, an extension to Lua scripts #8693 (comment)
Handled with FUCTION STATS command
Get the source code of a function: Suggestion: Redis Functions, an extension to Lua scripts #8693 (comment)
Handled with FUCTION INFO command
Community requests to consider for future planning
Notes
Note: Function creation/deletion is replicated to AOF but AOFRW is not implemented sense its going to be removed: #9794