Skip to content

Conversation

@yoav-steinberg
Copy link
Contributor

@yoav-steinberg yoav-steinberg commented Jan 17, 2022

In #10025 we added a mechanism for flagging certain properties for Redis Functions. This lead us to think we'd like to "port" this mechanism to Redis Scripts (EVAL) as well.

One good reason for this, other than the added functionality is because it addresses the poor behavior we currently have in EVAL in case the script performs a (non DENY_OOM) write operation during OOM state. See #8478 (And a previous attempt to handle it via #10093) for details. Note that in Redis Functions all write operations (including DEL) will return an error during OOM state unless the function is flagged as allow-oom in which case no OOM checking is performed at all.

This PR:

  • Enables setting EVAL (and SCRIPT LOAD) script flags as defined in Redis Functions Flags #10025.
  • Provides a syntactical framework via shebang for additional script annotations and even engine selection (instead of just lua) for scripts.
  • Provides backwards compatibility so scripts without the new annotations will behave as they did before.
  • Appropriate tests.
  • Changes EVAL[SHA]/_RO to be flagged as STALE commands. This makes it possible to flag individual scripts as allow-stale or not flag them as such. In backwards compatibility mode these commands will return the MASTERDOWN error as before.
  • Changes SCRIPT LOAD to be flagged as a STALE command. This is mainly to make it logically compatible with the change to EVAL in the previous point. It enables loading a script on a stale server which is technically okay it doesn't relate directly to the server's dataset. Running the script does, but that won't work unless the script is explicitly marked as allow-stale.

Even though not strictly required this should replace #10093. We also suggest closing #8478: using the shebang will cause the script to change its default behavior so it'll deny any writes during OOM state.

Usage example:

#!lua flags=no-writes,allow-stale
local x = redis.call('get','x')
return x

Note that even though the LUA syntax doesn't support hash tag comments .lua files do support a shebang tag on the top so they can be executed on Unix systems like any shell script. LUA's luaL_loadfile handles this as part of the LUA library. In the case of luaL_loadbuffer, which is what Redis uses, I needed to fix the input script in case of a shebang manually. I did this the same way luaL_loadfile does, by replacing the first line with a single line feed character.

@oranagra oranagra added release-notes indication that this issue needs to be mentioned in the release notes state:major-decision Requires core team consensus labels Jan 18, 2022
@zuiderkwast
Copy link
Contributor

zuiderkwast commented Jan 20, 2022

It's a pretty cool trick to use the shebang, but one would expect that it's also usable in a Unix system. A shebang starting with #!lua would invoke the lua executable but the flags=... argument makes no sense for the lua interpreter. How about a syntax where it actually makes sense, (or could make sense provided a local redis library is made available)? Maybe something like this?:

#!lua -l redis -e 'redis.flags("no-writes","allow-stale")'

@oranagra
Copy link
Member

@zuiderkwast the thing is that we want to be able to parse the flags and store them per script without executing it.
i.e. unlike the previous attempt, we can reject a write script on a read-only replica without even executing the Lua vm.

with your suggestion, we either execute that line to get the flags, or alternatively just attempt to parse it, but it'll be odd if it looks like Lua code where in fact we just parse it.

i'd rather use simple string splitting to get the flags (and maybe other args in the future.
the fact that it's not compatible with Unix CLI doesn't bother me.

@zuiderkwast
Copy link
Contributor

zuiderkwast commented Jan 20, 2022

i'd rather use simple string splitting to get the flags (and maybe other args in the future.
the fact that it's not compatible with Unix CLI doesn't bother me.

The fact that it's masquerading as something which it's isn't is potentially confusing.

How about allowing them within any kind of comment, with an additional "redis flags" tag on e.g. one of the two first lines of the script, so depending on the language, either of the below (idea, not exact syntax):

-- Redis flags=no-writes,allow-stale
/* Redis flags=no-writes,allow-stale */
# Redis flags=no-writes,allow-stale

Copy link
Member

@oranagra oranagra 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. obviously missing tests 8-)

yoav-steinberg and others added 6 commits January 21, 2022 14:04
- functions_flags -> script_flags
- use new behavior on shebang (even without flags)
Co-authored-by: Oran Agra <oran@redislabs.com>
@yoav-steinberg yoav-steinberg marked this pull request as ready for review January 21, 2022 12:55
Co-authored-by: Oran Agra <oran@redislabs.com>
@oranagra oranagra added the approval-needed Waiting for core team approval to be merged label Jan 23, 2022
@oranagra
Copy link
Member

@redis/core-team please approve.

@oranagra oranagra merged commit 7eadc5e into redis:unstable Jan 24, 2022
@oranagra
Copy link
Member

For the record, this was conceptually approved in a core-team meeting.

@oranagra
Copy link
Member

oranagra commented Feb 7, 2022

@yoav-steinberg i just realized that this broke the defragger.
it won't crash, but will fail defragging the actual script allocation.
while we're fixing this, maybe extend it to defrag the functions data structures?
mind fixing this?

yoav-steinberg added a commit to yoav-steinberg/redis that referenced this pull request Feb 9, 2022
In redis#10126 the global `lua_script` dict became a dict to a custom `luaScript` struct with an internal `robj` in it instead of a generic `sds` -> `robj` dict. This means we need custom code to defrag it and since scripts should never really cause much fragmentation it makes more sense to simply remove the defrag code for scripts.
yoav-steinberg added a commit to yoav-steinberg/redis that referenced this pull request Feb 10, 2022
oranagra pushed a commit that referenced this pull request Feb 11, 2022
Remove scripts defragger since it was broken since #10126 (released in 7.0 RC1).
would crash the server if defragger starts in a server that contains eval scripts.

In #10126 the global `lua_script` dict became a dict to a custom `luaScript` struct with an internal `robj`
in it instead of a generic `sds` -> `robj` dict. This means we need custom code to defrag it and since scripts
should never really cause much fragmentation it makes more sense to simply remove the defrag code for scripts.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approval-needed Waiting for core team approval to be merged release-notes indication that this issue needs to be mentioned in the release notes state:major-decision Requires core team consensus

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

[BUG] Lua script doesn't return OOM when making arbitrary delete command first

6 participants