summaryrefslogtreecommitdiff
path: root/src/functions.c
diff options
context:
space:
mode:
authoryoav-steinberg <yoav@monfort.co.il>2022-01-24 16:50:02 +0200
committerGitHub <noreply@github.com>2022-01-24 16:50:02 +0200
commit7eadc5ee7062c6de00323c1b8a9598d1b5684217 (patch)
tree5c0286bb7bd3f8689f2a346f389f006e64e3477e /src/functions.c
parent857dc5bacd85a9a4c31b7ef9eb350690ca0a85ad (diff)
downloadredis-7eadc5ee7062c6de00323c1b8a9598d1b5684217.tar.gz
Support function flags in script EVAL via shebang header (#10126)
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 #10025. - Provides a syntactical framework via [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) 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`. 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.
Diffstat (limited to 'src/functions.c')
-rw-r--r--src/functions.c62
1 files changed, 2 insertions, 60 deletions
diff --git a/src/functions.c b/src/functions.c
index 90e36231e..1a94bacc3 100644
--- a/src/functions.c
+++ b/src/functions.c
@@ -570,69 +570,11 @@ static void fcallCommandGeneric(client *c, int ro) {
return;
}
- if ((fi->f_flags & SCRIPT_FLAG_NO_CLUSTER) && server.cluster_enabled) {
- addReplyError(c, "Can not run function on cluster, 'no-cluster' flag is set.");
- return;
- }
-
- if (!(fi->f_flags & SCRIPT_FLAG_ALLOW_OOM) && server.script_oom && server.maxmemory) {
- addReplyError(c, "-OOM allow-oom flag is not set on the function, "
- "can not run it when used memory > 'maxmemory'");
- return;
- }
+ scriptRunCtx run_ctx;
- if (server.masterhost && server.repl_state != REPL_STATE_CONNECTED &&
- server.repl_serve_stale_data == 0 && !(fi->f_flags & SCRIPT_FLAG_ALLOW_STALE))
- {
- addReplyError(c, "-MASTERDOWN Link with MASTER is down, "
- "replica-serve-stale-data is set to 'no' "
- "and 'allow-stale' flag is not set on the function.");
+ if (scriptPrepareForRun(&run_ctx, fi->li->ei->c, c, fi->name, fi->f_flags, ro) != C_OK)
return;
- }
-
- if (!(fi->f_flags & SCRIPT_FLAG_NO_WRITES)) {
- /* Function may perform writes we need to verify:
- * 1. we are not a readonly replica
- * 2. no disk error detected
- * 3. command is not 'fcall_ro' */
- if (server.masterhost && server.repl_slave_ro && c->id != CLIENT_ID_AOF
- && !(c->flags & CLIENT_MASTER))
- {
- addReplyError(c, "Can not run a function with write flag on readonly replica");
- return;
- }
- int deny_write_type = writeCommandsDeniedByDiskError();
- if (deny_write_type != DISK_ERROR_TYPE_NONE && server.masterhost == NULL) {
- if (deny_write_type == DISK_ERROR_TYPE_RDB)
- addReplyError(c, "-MISCONF Redis is configured to save RDB snapshots, "
- "but it is currently not able to persist on disk. "
- "So its impossible to run functions that has 'write' flag on.");
- else
- addReplyErrorFormat(c, "-MISCONF Redis is configured to persist data to AOF, "
- "but it is currently not able to persist on disk. "
- "So its impossible to run functions that has 'write' flag on. "
- "AOF error: %s", strerror(server.aof_last_write_errno));
- return;
- }
-
- if (ro) {
- addReplyError(c, "Can not execute a function with write flag using fcall_ro.");
- return;
- }
- }
-
- scriptRunCtx run_ctx;
-
- scriptPrepareForRun(&run_ctx, fi->li->ei->c, c, fi->name);
- if (ro || (fi->f_flags & SCRIPT_FLAG_NO_WRITES)) {
- /* On fcall_ro or on functions that do not have the 'write'
- * flag, we will not allow write commands. */
- run_ctx.flags |= SCRIPT_READ_ONLY;
- }
- if (fi->f_flags & SCRIPT_FLAG_ALLOW_OOM) {
- run_ctx.flags |= SCRIPT_ALLOW_OOM;
- }
engine->call(&run_ctx, engine->engine_ctx, fi->function, c->argv + 3, numkeys,
c->argv + 3 + numkeys, c->argc - 3 - numkeys);
scriptResetRun(&run_ctx);