mirror of
https://github.com/redis/redis.git
synced 2026-04-21 03:01:35 -04:00
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.
111 lines
4.5 KiB
C
111 lines
4.5 KiB
C
/*
|
|
* Copyright (c) 2009-2021, Redis Ltd.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of Redis nor the names of its contributors may be used
|
|
* to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef __SCRIPT_H_
|
|
#define __SCRIPT_H_
|
|
|
|
/*
|
|
* Script.c unit provides an API for functions and eval
|
|
* to interact with Redis. Interaction includes mostly
|
|
* executing commands, but also functionalities like calling
|
|
* Redis back on long scripts or check if the script was killed.
|
|
*
|
|
* The interaction is done using a scriptRunCtx object that
|
|
* need to be created by the user and initialized using scriptPrepareForRun.
|
|
*
|
|
* Detailed list of functionalities expose by the unit:
|
|
* 1. Calling commands (including all the validation checks such as
|
|
* acl, cluster, read only run, ...)
|
|
* 2. Set Resp
|
|
* 3. Set Replication method (AOF/REPLICATION/NONE)
|
|
* 4. Call Redis back to on long running scripts to allow Redis reply
|
|
* to clients and perform script kill
|
|
*/
|
|
|
|
/*
|
|
* scriptInterrupt function will return one of those value,
|
|
*
|
|
* - SCRIPT_KILL - kill the current running script.
|
|
* - SCRIPT_CONTINUE - keep running the current script.
|
|
*/
|
|
#define SCRIPT_KILL 1
|
|
#define SCRIPT_CONTINUE 2
|
|
|
|
/* runCtx flags */
|
|
#define SCRIPT_WRITE_DIRTY (1ULL<<0) /* indicate that the current script already performed a write command */
|
|
#define SCRIPT_TIMEDOUT (1ULL<<3) /* indicate that the current script timedout */
|
|
#define SCRIPT_KILLED (1ULL<<4) /* indicate that the current script was marked to be killed */
|
|
#define SCRIPT_READ_ONLY (1ULL<<5) /* indicate that the current script should only perform read commands */
|
|
#define SCRIPT_ALLOW_OOM (1ULL<<6) /* indicate to allow any command even if OOM reached */
|
|
#define SCRIPT_EVAL_MODE (1ULL<<7) /* Indicate that the current script called from legacy Lua */
|
|
typedef struct scriptRunCtx scriptRunCtx;
|
|
|
|
struct scriptRunCtx {
|
|
const char *funcname;
|
|
client *c;
|
|
client *original_client;
|
|
int flags;
|
|
int repl_flags;
|
|
monotime start_time;
|
|
mstime_t snapshot_time;
|
|
};
|
|
|
|
/* Scripts flags */
|
|
#define SCRIPT_FLAG_NO_WRITES (1ULL<<0)
|
|
#define SCRIPT_FLAG_ALLOW_OOM (1ULL<<1)
|
|
#define SCRIPT_FLAG_ALLOW_STALE (1ULL<<2)
|
|
#define SCRIPT_FLAG_NO_CLUSTER (1ULL<<3)
|
|
#define SCRIPT_FLAG_EVAL_COMPAT_MODE (1ULL<<4) /* EVAL Script backwards compatible behavior, no shebang provided */
|
|
|
|
/* Defines a script flags */
|
|
typedef struct scriptFlag {
|
|
uint64_t flag;
|
|
const char *str;
|
|
} scriptFlag;
|
|
|
|
extern scriptFlag scripts_flags_def[];
|
|
|
|
int scriptPrepareForRun(scriptRunCtx *r_ctx, client *engine_client, client *caller, const char *funcname, uint64_t script_flags, int ro);
|
|
void scriptResetRun(scriptRunCtx *r_ctx);
|
|
int scriptSetResp(scriptRunCtx *r_ctx, int resp);
|
|
int scriptSetRepl(scriptRunCtx *r_ctx, int repl);
|
|
void scriptCall(scriptRunCtx *r_ctx, robj **argv, int argc, sds *err);
|
|
int scriptInterrupt(scriptRunCtx *r_ctx);
|
|
void scriptKill(client *c, int is_eval);
|
|
int scriptIsRunning();
|
|
const char* scriptCurrFunction();
|
|
int scriptIsEval();
|
|
int scriptIsTimedout();
|
|
client* scriptGetClient();
|
|
client* scriptGetCaller();
|
|
mstime_t scriptTimeSnapshot();
|
|
long long scriptRunDuration();
|
|
|
|
#endif /* __SCRIPT_H_ */
|