From e0cd580aefe13e49df802fec5135e4f22d46e758 Mon Sep 17 00:00:00 2001 From: "meir@redislabs.com" Date: Tue, 5 Oct 2021 17:03:12 +0300 Subject: [PATCH] Redis Functions - Move Lua related variable into luaCtx struct The following variable was renamed: 1. lua_caller -> script_caller 2. lua_time_limit -> script_time_limit 3. lua_timedout -> script_timedout 4. lua_oom -> script_oom 5. lua_disable_deny_script -> script_disable_deny_script 6. in_eval -> in_script The following variables was moved to lctx under eval.c 1. lua 2. lua_client 3. lua_cur_script 4. lua_scripts 5. lua_scripts_mem 6. lua_replicate_commands 7. lua_write_dirty 8. lua_random_dirty 9. lua_multi_emitted 10. lua_repl 11. lua_kill 12. lua_time_start 13. lua_time_snapshot This commit is in a low risk of introducing any issues and it is just moving varibales around and not changing any logic. --- src/acl.c | 2 +- src/config.c | 2 +- src/db.c | 4 +- src/debug.c | 2 +- src/defrag.c | 2 +- src/eval.c | 171 +++++++++++++++++++++++++++-------------------- src/evict.c | 2 +- src/module.c | 8 +-- src/networking.c | 2 +- src/object.c | 6 +- src/rdb.c | 6 +- src/script_lua.c | 82 +++++++++++------------ src/script_lua.h | 65 ++++++++++++++++++ src/server.c | 30 ++++----- src/server.h | 39 +++++------ 15 files changed, 253 insertions(+), 170 deletions(-) create mode 100644 src/script_lua.h diff --git a/src/acl.c b/src/acl.c index 5d1598b39..9c23cffa8 100644 --- a/src/acl.c +++ b/src/acl.c @@ -1897,7 +1897,7 @@ void addACLLogEntry(client *c, int reason, int context, int argpos, sds username } client *realclient = c; - if (realclient->flags & CLIENT_LUA) realclient = server.lua_caller; + if (realclient->flags & CLIENT_LUA) realclient = server.script_caller; le->cinfo = catClientInfoString(sdsempty(),realclient); le->context = context; diff --git a/src/config.c b/src/config.c index 2b2ff737d..939a5342c 100644 --- a/src/config.c +++ b/src/config.c @@ -2650,7 +2650,7 @@ standardConfig configs[] = { createULongConfig("acllog-max-len", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.acllog_max_len, 128, INTEGER_CONFIG, NULL, NULL), /* Long Long configs */ - createLongLongConfig("lua-time-limit", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.lua_time_limit, 5000, INTEGER_CONFIG, NULL, NULL),/* milliseconds */ + createLongLongConfig("script-time-limit", "lua-time-limit", MODIFIABLE_CONFIG, 0, LONG_MAX, server.script_time_limit, 5000, INTEGER_CONFIG, NULL, NULL),/* milliseconds */ createLongLongConfig("cluster-node-timeout", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.cluster_node_timeout, 15000, INTEGER_CONFIG, NULL, NULL), createLongLongConfig("slowlog-log-slower-than", NULL, MODIFIABLE_CONFIG, -1, LLONG_MAX, server.slowlog_log_slower_than, 10000, INTEGER_CONFIG, NULL, NULL), createLongLongConfig("latency-monitor-threshold", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.latency_monitor_threshold, 0, INTEGER_CONFIG, NULL, NULL), diff --git a/src/db.c b/src/db.c index b98310cf3..749f5535b 100644 --- a/src/db.c +++ b/src/db.c @@ -1501,8 +1501,8 @@ int keyIsExpired(redisDb *db, robj *key) { * only the first time it is accessed and not in the middle of the * script execution, making propagation to slaves / AOF consistent. * See issue #1525 on Github for more information. */ - if (server.lua_caller) { - now = server.lua_time_snapshot; + if (server.script_caller) { + now = evalTimeSnapshot(); } /* If we are in the middle of a command execution, we still want to use * a reference time that does not change: in that case we just use the diff --git a/src/debug.c b/src/debug.c index 8776de38f..d4f3f5dd2 100644 --- a/src/debug.c +++ b/src/debug.c @@ -919,7 +919,7 @@ NULL addReplyStatus(c,"Apparently Redis did not crash: test passed"); } else if (!strcasecmp(c->argv[1]->ptr,"set-disable-deny-scripts") && c->argc == 3) { - server.lua_disable_deny_script = atoi(c->argv[2]->ptr);; + server.script_disable_deny_script = atoi(c->argv[2]->ptr);; addReply(c,shared.ok); } else if (!strcasecmp(c->argv[1]->ptr,"config-rewrite-force-all") && c->argc == 2) { diff --git a/src/defrag.c b/src/defrag.c index 734174c3a..ffd6eaba6 100644 --- a/src/defrag.c +++ b/src/defrag.c @@ -939,7 +939,7 @@ long defragOtherGlobals() { /* there are many more pointers to defrag (e.g. client argv, output / aof buffers, etc. * but we assume most of these are short lived, we only need to defrag allocations * that remain static for a long time */ - defragged += activeDefragSdsDict(server.lua_scripts, DEFRAG_SDS_DICT_VAL_IS_STROB); + defragged += activeDefragSdsDict(evalScriptsDict(), DEFRAG_SDS_DICT_VAL_IS_STROB); defragged += activeDefragSdsListAndDict(server.repl_scriptcache_fifo, server.repl_scriptcache_dict, DEFRAG_SDS_DICT_NO_VAL); defragged += moduleDefragGlobals(); return defragged; diff --git a/src/eval.c b/src/eval.c index 6e6aeac7c..5c4c419b5 100644 --- a/src/eval.c +++ b/src/eval.c @@ -50,6 +50,9 @@ void ldbLog(sds entry); void ldbLogRedisReply(char *reply); sds ldbCatStackValue(sds s, lua_State *lua, int idx); +/* Lua context */ +luaCtx lctx; + /* Debugger shared state is stored inside this global structure. */ #define LDB_BREAKPOINTS_MAX 64 /* Max number of breakpoints. */ #define LDB_MAX_LEN_DEFAULT 256 /* Default len limit for replies / var dumps. */ @@ -138,10 +141,10 @@ int luaRedisDebugCommand(lua_State *lua) { * already started to write, returns false and stick to whole scripts * replication, which is our default. */ int luaRedisReplicateCommandsCommand(lua_State *lua) { - if (server.lua_write_dirty) { + if (lctx.lua_write_dirty) { lua_pushboolean(lua,0); } else { - server.lua_replicate_commands = 1; + lctx.lua_replicate_commands = 1; /* When we switch to single commands replication, we can provide * different math.random() sequences at every call, which is what * the user normally expects. */ @@ -165,19 +168,19 @@ void scriptingInit(int setup) { lua_State *lua = lua_open(); if (setup) { - server.lua_client = NULL; - server.lua_caller = NULL; - server.lua_cur_script = NULL; - server.lua_timedout = 0; - server.lua_disable_deny_script = 0; + lctx.lua_client = NULL; + server.script_caller = NULL; + lctx.lua_cur_script = NULL; + server.script_timedout = 0; + server.script_disable_deny_script = 0; ldbInit(); } /* Initialize a dictionary we use to map SHAs to scripts. * This is useful for replication, as we need to replicate EVALSHA * as EVAL, so we need to remember the associated script. */ - server.lua_scripts = dictCreate(&shaScriptObjectDictType); - server.lua_scripts_mem = 0; + lctx.lua_scripts = dictCreate(&shaScriptObjectDictType); + lctx.lua_scripts_mem = 0; luaEngineRegisterRedisAPI(lua); @@ -238,12 +241,12 @@ void scriptingInit(int setup) { * inside the Lua interpreter. * Note: there is no need to create it again when this function is called * by scriptingReset(). */ - if (server.lua_client == NULL) { - server.lua_client = createClient(NULL); - server.lua_client->flags |= CLIENT_LUA; + if (lctx.lua_client == NULL) { + lctx.lua_client = createClient(NULL); + lctx.lua_client->flags |= CLIENT_LUA; /* We do not want to allow blocking commands inside Lua */ - server.lua_client->flags |= CLIENT_DENY_BLOCKING; + lctx.lua_client->flags |= CLIENT_DENY_BLOCKING; } /* Lua beginners often don't use "local", this is likely to introduce @@ -251,18 +254,18 @@ void scriptingInit(int setup) { * to global variables. */ scriptingEnableGlobalsProtection(lua); - server.lua = lua; + lctx.lua = lua; } /* Release resources related to Lua scripting. * This function is used in order to reset the scripting environment. */ void scriptingRelease(int async) { if (async) - freeLuaScriptsAsync(server.lua_scripts); + freeLuaScriptsAsync(lctx.lua_scripts); else - dictRelease(server.lua_scripts); - server.lua_scripts_mem = 0; - lua_close(server.lua); + dictRelease(lctx.lua_scripts); + lctx.lua_scripts_mem = 0; + lua_close(lctx.lua); } void scriptingReset(int async) { @@ -291,7 +294,7 @@ void scriptingReset(int async) { * * If 'c' is not NULL, on error the client is informed with an appropriate * error describing the nature of the problem and the Lua interpreter error. */ -sds luaCreateFunction(client *c, lua_State *lua, robj *body) { +sds luaCreateFunction(client *c, robj *body) { char funcname[43]; dictEntry *de; @@ -300,7 +303,7 @@ sds luaCreateFunction(client *c, lua_State *lua, robj *body) { sha1hex(funcname+2,body->ptr,sdslen(body->ptr)); sds sha = sdsnewlen(funcname+2,40); - if ((de = dictFind(server.lua_scripts,sha)) != NULL) { + if ((de = dictFind(lctx.lua_scripts,sha)) != NULL) { sdsfree(sha); return dictGetKey(de); } @@ -312,25 +315,25 @@ sds luaCreateFunction(client *c, lua_State *lua, robj *body) { funcdef = sdscatlen(funcdef,body->ptr,sdslen(body->ptr)); funcdef = sdscatlen(funcdef,"\nend",4); - if (luaL_loadbuffer(lua,funcdef,sdslen(funcdef),"@user_script")) { + if (luaL_loadbuffer(lctx.lua,funcdef,sdslen(funcdef),"@user_script")) { if (c != NULL) { addReplyErrorFormat(c, "Error compiling script (new function): %s\n", - lua_tostring(lua,-1)); + lua_tostring(lctx.lua,-1)); } - lua_pop(lua,1); + lua_pop(lctx.lua,1); sdsfree(sha); sdsfree(funcdef); return NULL; } sdsfree(funcdef); - if (lua_pcall(lua,0,0,0)) { + if (lua_pcall(lctx.lua,0,0,0)) { if (c != NULL) { addReplyErrorFormat(c,"Error running script (new function): %s\n", - lua_tostring(lua,-1)); + lua_tostring(lctx.lua,-1)); } - lua_pop(lua,1); + lua_pop(lctx.lua,1); sdsfree(sha); return NULL; } @@ -338,31 +341,31 @@ sds luaCreateFunction(client *c, lua_State *lua, robj *body) { /* We also save a SHA1 -> Original script map in a dictionary * so that we can replicate / write in the AOF all the * EVALSHA commands as EVAL using the original script. */ - int retval = dictAdd(server.lua_scripts,sha,body); - serverAssertWithInfo(c ? c : server.lua_client,NULL,retval == DICT_OK); - server.lua_scripts_mem += sdsZmallocSize(sha) + getStringObjectSdsUsedMemory(body); + int retval = dictAdd(lctx.lua_scripts,sha,body); + serverAssertWithInfo(c ? c : lctx.lua_client,NULL,retval == DICT_OK); + lctx.lua_scripts_mem += sdsZmallocSize(sha) + getStringObjectSdsUsedMemory(body); incrRefCount(body); return sha; } void prepareLuaClient(void) { /* Select the right DB in the context of the Lua client */ - selectDb(server.lua_client,server.lua_caller->db->id); - server.lua_client->resp = 2; /* Default is RESP2, scripts can change it. */ + selectDb(lctx.lua_client,server.script_caller->db->id); + lctx.lua_client->resp = 2; /* Default is RESP2, scripts can change it. */ /* If we are in MULTI context, flag Lua client as CLIENT_MULTI. */ - if (server.lua_caller->flags & CLIENT_MULTI) { - server.lua_client->flags |= CLIENT_MULTI; + if (server.script_caller->flags & CLIENT_MULTI) { + lctx.lua_client->flags |= CLIENT_MULTI; } } void resetLuaClient(void) { /* After the script done, remove the MULTI state. */ - server.lua_client->flags &= ~CLIENT_MULTI; + lctx.lua_client->flags &= ~CLIENT_MULTI; } void evalGenericCommand(client *c, int evalsha) { - lua_State *lua = server.lua; + lua_State *lua = lctx.lua; char funcname[43]; long long numkeys; long long initial_server_dirty = server.dirty; @@ -380,11 +383,11 @@ void evalGenericCommand(client *c, int evalsha) { * * Thanks to this flag we'll raise an error every time a write command * is called after a random command was used. */ - server.lua_random_dirty = 0; - server.lua_write_dirty = 0; - server.lua_replicate_commands = server.lua_always_replicate_commands; - server.lua_multi_emitted = 0; - server.lua_repl = PROPAGATE_AOF|PROPAGATE_REPL; + lctx.lua_random_dirty = 0; + lctx.lua_write_dirty = 0; + lctx.lua_replicate_commands = server.lua_always_replicate_commands; + lctx.lua_multi_emitted = 0; + lctx.lua_repl = PROPAGATE_AOF|PROPAGATE_REPL; /* Get the number of arguments that are keys */ if (getLongLongFromObjectOrReply(c,c->argv[2],&numkeys,NULL) != C_OK) @@ -433,7 +436,7 @@ void evalGenericCommand(client *c, int evalsha) { addReplyErrorObject(c, shared.noscripterr); return; } - if (luaCreateFunction(c,lua,c->argv[1]) == NULL) { + if (luaCreateFunction(c,c->argv[1]) == NULL) { lua_pop(lua,1); /* remove the error handler from the stack. */ /* The error is sent to the client by luaCreateFunction() * itself when it returns NULL. */ @@ -456,17 +459,17 @@ void evalGenericCommand(client *c, int evalsha) { * * If we are debugging, we set instead a "line" hook so that the * debugger is call-back at every line executed by the script. */ - server.in_eval = 1; - server.lua_caller = c; - server.lua_cur_script = funcname + 2; - server.lua_time_start = getMonotonicUs(); - server.lua_time_snapshot = mstime(); - server.lua_kill = 0; - if (server.lua_time_limit > 0 && ldb.active == 0) { + server.in_script = 1; + server.script_caller = c; + lctx.lua_cur_script = funcname + 2; + lctx.lua_time_start = getMonotonicUs(); + lctx.lua_time_snapshot = mstime(); + lctx.lua_kill = 0; + if (server.script_time_limit > 0 && ldb.active == 0) { lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000); delhook = 1; } else if (ldb.active) { - lua_sethook(server.lua,luaLdbLineHook,LUA_MASKLINE|LUA_MASKCOUNT,100000); + lua_sethook(lctx.lua,luaLdbLineHook,LUA_MASKLINE|LUA_MASKCOUNT,100000); delhook = 1; } @@ -481,8 +484,8 @@ void evalGenericCommand(client *c, int evalsha) { /* Perform some cleanup that we need to do both on error and success. */ if (delhook) lua_sethook(lua,NULL,0,0); /* Disable hook */ - if (server.lua_timedout) { - server.lua_timedout = 0; + if (server.script_timedout) { + server.script_timedout = 0; blockingOperationEnds(); /* Restore the client that was protected when the script timeout * was detected. */ @@ -490,9 +493,9 @@ void evalGenericCommand(client *c, int evalsha) { if (server.masterhost && server.master) queueClientForReprocessing(server.master); } - server.in_eval = 0; - server.lua_caller = NULL; - server.lua_cur_script = NULL; + server.in_script = 0; + server.script_caller = NULL; + lctx.lua_cur_script = NULL; /* Call the Lua garbage collector from time to time to avoid a * full cycle performed by Lua, which adds too latency. @@ -524,9 +527,9 @@ void evalGenericCommand(client *c, int evalsha) { /* If we are using single commands replication, emit EXEC if there * was at least a write. */ - if (server.lua_replicate_commands) { + if (lctx.lua_replicate_commands) { preventCommandPropagation(c); - if (server.lua_multi_emitted) { + if (lctx.lua_multi_emitted) { execCommandPropagateExec(c->db->id); } } @@ -541,12 +544,12 @@ void evalGenericCommand(client *c, int evalsha) { * For replication, every time a new slave attaches to the master, we need to * flush our cache of scripts that can be replicated as EVALSHA, while * for AOF we need to do so every time we rewrite the AOF file. */ - if (evalsha && !server.lua_replicate_commands) { + if (evalsha && !lctx.lua_replicate_commands) { if (!replicationScriptCacheExists(c->argv[1]->ptr)) { /* This script is not in our script cache, replicate it as * EVAL, then add it into the script cache, as from now on * slaves and AOF know about it. */ - robj *script = dictFetchValue(server.lua_scripts,c->argv[1]->ptr); + robj *script = dictFetchValue(lctx.lua_scripts,c->argv[1]->ptr); replicationScriptCacheAdd(c->argv[1]->ptr); serverAssertWithInfo(c,NULL,script != NULL); @@ -648,25 +651,25 @@ NULL addReplyArrayLen(c, c->argc-2); for (j = 2; j < c->argc; j++) { - if (dictFind(server.lua_scripts,c->argv[j]->ptr)) + if (dictFind(lctx.lua_scripts,c->argv[j]->ptr)) addReply(c,shared.cone); else addReply(c,shared.czero); } } else if (c->argc == 3 && !strcasecmp(c->argv[1]->ptr,"load")) { - sds sha = luaCreateFunction(c,server.lua,c->argv[2]); + sds sha = luaCreateFunction(c,c->argv[2]); if (sha == NULL) return; /* The error was sent by luaCreateFunction(). */ addReplyBulkCBuffer(c,sha,40); forceCommandPropagation(c,PROPAGATE_REPL|PROPAGATE_AOF); } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"kill")) { - if (server.lua_caller == NULL) { + if (server.script_caller == NULL) { addReplyError(c,"-NOTBUSY No scripts in execution right now."); - } else if (server.lua_caller->flags & CLIENT_MASTER) { + } else if (server.script_caller->flags & CLIENT_MASTER) { addReplyError(c,"-UNKILLABLE The busy script was sent by a master instance in the context of replication and cannot be killed."); - } else if (server.lua_write_dirty) { + } else if (lctx.lua_write_dirty) { addReplyError(c,"-UNKILLABLE Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in a hard way using the SHUTDOWN NOSAVE command."); } else { - server.lua_kill = 1; + lctx.lua_kill = 1; addReply(c,shared.ok); } } else if (c->argc == 3 && !strcasecmp(c->argv[1]->ptr,"debug")) { @@ -693,6 +696,26 @@ NULL } } +unsigned long evalMemory() { + return lua_gc(lctx.lua, LUA_GCCOUNT, 0) * 1024LL; +} + +dict* evalScriptsDict() { + return lctx.lua_scripts; +} + +unsigned long evalScriptsMemory() { + return lctx.lua_scripts_mem + + dictSize(lctx.lua_scripts) * sizeof(dictEntry) + + dictSlots(lctx.lua_scripts) * sizeof(dictEntry*); +} + +/* Returns the time when the script invocation started */ +mstime_t evalTimeSnapshot() { + return lctx.lua_time_snapshot; +} + + /* --------------------------------------------------------------------------- * LDB: Redis Lua debugging facilities * ------------------------------------------------------------------------- */ @@ -717,6 +740,10 @@ void ldbFlushLog(list *log) { listDelNode(log,ln); } +int ldbIsEnabled(){ + return ldb.active && ldb.step; +} + /* Enable debug mode of Lua scripts for this client. */ void ldbEnable(client *c) { c->flags |= CLIENT_LUA_DEBUG; @@ -1447,7 +1474,7 @@ void ldbEval(lua_State *lua, sds *argv, int argc) { * implementation, with ldb.step enabled, so as a side effect the Redis command * and its reply are logged. */ void ldbRedis(lua_State *lua, sds *argv, int argc) { - int j, saved_rc = server.lua_replicate_commands; + int j, saved_rc = lctx.lua_replicate_commands; if (!lua_checkstack(lua, argc + 1)) { /* Increase the Lua stack if needed to make sure there is enough room @@ -1466,10 +1493,10 @@ void ldbRedis(lua_State *lua, sds *argv, int argc) { for (j = 1; j < argc; j++) lua_pushlstring(lua,argv[j],sdslen(argv[j])); ldb.step = 1; /* Force redis.call() to log. */ - server.lua_replicate_commands = 1; + lctx.lua_replicate_commands = 1; lua_pcall(lua,argc-1,1,0); /* Stack: redis, result */ ldb.step = 0; /* Disable logging. */ - server.lua_replicate_commands = saved_rc; + lctx.lua_replicate_commands = saved_rc; lua_pop(lua,2); /* Discard the result and clean the stack. */ } @@ -1657,9 +1684,9 @@ void luaLdbLineHook(lua_State *lua, lua_Debug *ar) { /* Check if a timeout occurred. */ if (ar->event == LUA_HOOKCOUNT && ldb.step == 0 && bp == 0) { - mstime_t elapsed = elapsedMs(server.lua_time_start); - mstime_t timelimit = server.lua_time_limit ? - server.lua_time_limit : 5000; + mstime_t elapsed = elapsedMs(server.script_time_limit); + mstime_t timelimit = server.script_time_limit ? + server.script_time_limit : 5000; if (elapsed >= timelimit) { timeout = 1; ldb.step = 1; @@ -1687,7 +1714,7 @@ void luaLdbLineHook(lua_State *lua, lua_Debug *ar) { lua_pushstring(lua, "timeout during Lua debugging with client closing connection"); lua_error(lua); } - server.lua_time_start = getMonotonicUs(); - server.lua_time_snapshot = mstime(); + lctx.lua_time_start = getMonotonicUs(); + lctx.lua_time_snapshot = mstime(); } } diff --git a/src/evict.c b/src/evict.c index 4186378a2..2d87546cb 100644 --- a/src/evict.c +++ b/src/evict.c @@ -472,7 +472,7 @@ static int evictionTimeProc( static int isSafeToPerformEvictions(void) { /* - There must be no script in timeout condition. * - Nor we are loading data right now. */ - if (server.lua_timedout || server.loading) return 0; + if (server.script_timedout || server.loading) return 0; /* By default replicas should ignore maxmemory * and just be masters exact copies. */ diff --git a/src/module.c b/src/module.c index 7c1cc054b..bc7599b75 100644 --- a/src/module.c +++ b/src/module.c @@ -629,7 +629,7 @@ void moduleHandlePropagationAfterCommandCallback(RedisModuleCtx *ctx) { /* If this command is executed from with Lua or MULTI/EXEC we do not * need to propagate EXEC */ - if (server.in_eval || server.in_exec) return; + if (server.in_script || server.in_exec) return; /* Handle the replication of the final EXEC, since whatever a command * emits is always wrapped around MULTI/EXEC. */ @@ -2333,7 +2333,7 @@ int RM_ReplyWithLongDouble(RedisModuleCtx *ctx, long double ld) { void moduleReplicateMultiIfNeeded(RedisModuleCtx *ctx) { /* Skip this if client explicitly wrap the command with MULTI, or if * the module command was called by a script. */ - if (server.in_eval || server.in_exec) return; + if (server.in_script || server.in_exec) return; /* If we already emitted MULTI return ASAP. */ if (server.propagate_in_transaction) return; /* If this is a thread safe context, we do not want to wrap commands @@ -2709,7 +2709,7 @@ int RM_GetContextFlags(RedisModuleCtx *ctx) { } } - if (server.in_eval) + if (server.in_script) flags |= REDISMODULE_CTX_FLAGS_LUA; if (server.in_exec) @@ -6215,7 +6215,7 @@ void unblockClientFromModule(client *c) { */ RedisModuleBlockedClient *moduleBlockClient(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata) { client *c = ctx->client; - int islua = server.in_eval; + int islua = server.in_script; int ismulti = server.in_exec; c->bpop.module_blocked_handle = zmalloc(sizeof(RedisModuleBlockedClient)); diff --git a/src/networking.c b/src/networking.c index 1f1a2e169..fb4a487a3 100644 --- a/src/networking.c +++ b/src/networking.c @@ -2199,7 +2199,7 @@ int processInputBuffer(client *c) { * condition on the slave. We want just to accumulate the replication * stream (instead of replying -BUSY like we do with other clients) and * later resume the processing. */ - if (server.lua_timedout && c->flags & CLIENT_MASTER) break; + if (server.script_timedout && c->flags & CLIENT_MASTER) break; /* CLIENT_CLOSE_AFTER_REPLY closes the connection once the reply is * written to the client. Make sure to not let the reply grow after diff --git a/src/object.c b/src/object.c index 5831f196d..dab0648a8 100644 --- a/src/object.c +++ b/src/object.c @@ -1203,9 +1203,7 @@ struct redisMemOverhead *getMemoryOverheadData(void) { mh->aof_buffer = mem; mem_total+=mem; - mem = server.lua_scripts_mem; - mem += dictSize(server.lua_scripts) * sizeof(dictEntry) + - dictSlots(server.lua_scripts) * sizeof(dictEntry*); + mem = evalScriptsMemory(); mem += dictSize(server.repl_scriptcache_dict) * sizeof(dictEntry) + dictSlots(server.repl_scriptcache_dict) * sizeof(dictEntry*); if (listLength(server.repl_scriptcache_fifo) > 0) { @@ -1325,7 +1323,7 @@ sds getMemoryDoctorReport(void) { } /* Too many scripts are cached? */ - if (dictSize(server.lua_scripts) > 1000) { + if (dictSize(evalScriptsDict()) > 1000) { many_scripts = 1; num_reports++; } diff --git a/src/rdb.c b/src/rdb.c index 4a65f2cc9..dcbc83785 100644 --- a/src/rdb.c +++ b/src/rdb.c @@ -1303,8 +1303,8 @@ int rdbSaveRio(rio *rdb, int *error, int rdbflags, rdbSaveInfo *rsi) { * the script cache as well: on successful PSYNC after a restart, we need * to be able to process any EVALSHA inside the replication backlog the * master will send us. */ - if (rsi && dictSize(server.lua_scripts)) { - di = dictGetIterator(server.lua_scripts); + if (rsi && dictSize(evalScriptsDict())) { + di = dictGetIterator(evalScriptsDict()); while((de = dictNext(di)) != NULL) { robj *body = dictGetVal(de); if (rdbSaveAuxField(rdb,"lua",3,body->ptr,sdslen(body->ptr)) == -1) @@ -2808,7 +2808,7 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi, redisDb *dbarray) { if (rsi) rsi->repl_offset = strtoll(auxval->ptr,NULL,10); } else if (!strcasecmp(auxkey->ptr,"lua")) { /* Load the script back in memory. */ - if (luaCreateFunction(NULL,server.lua,auxval) == NULL) { + if (luaCreateFunction(NULL, auxval) == NULL) { rdbReportCorruptRDB( "Can't load Lua script from RDB file! " "BODY: %s", (char*)auxval->ptr); diff --git a/src/script_lua.c b/src/script_lua.c index 0685a4bb3..bee5d6c93 100644 --- a/src/script_lua.c +++ b/src/script_lua.c @@ -399,7 +399,7 @@ void luaPushError(lua_State *lua, char *error) { /* If debugging is active and in step mode, log errors resulting from * Redis commands. */ - if (ldb.active && ldb.step) { + if (ldbIsEnabled()) { ldbLog(sdscatprintf(sdsempty()," %s",error)); } @@ -483,7 +483,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) { addReplyBulkCBuffer(c,(char*)lua_tostring(lua,-1),lua_strlen(lua,-1)); break; case LUA_TBOOLEAN: - if (server.lua_client->resp == 2) + if (lctx.lua_client->resp == 2) addReply(c,lua_toboolean(lua,-1) ? shared.cone : shared.null[c->resp]); else @@ -653,7 +653,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) { int luaRedisGenericCommand(lua_State *lua, int raise_error) { int j, argc = lua_gettop(lua); struct redisCommand *cmd; - client *c = server.lua_client; + client *c = lctx.lua_client; sds reply; /* Cached across calls. */ @@ -744,7 +744,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) { /* Setup our fake client for command execution */ c->argv = argv; c->argc = argc; - c->user = server.lua_caller->user; + c->user = server.script_caller->user; /* Process module hooks */ moduleCallCommandFilters(c); @@ -752,7 +752,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) { argc = c->argc; /* Log the command if debugging is active. */ - if (ldb.active && ldb.step) { + if (ldbIsEnabled()) { sds cmdlog = sdsnew(""); for (j = 0; j < c->argc; j++) { if (j == 10) { @@ -782,14 +782,14 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) { c->cmd = c->lastcmd = cmd; /* There are commands that are not allowed inside scripts. */ - if (!server.lua_disable_deny_script && (cmd->flags & CMD_NOSCRIPT)) { + if (!server.script_disable_deny_script && (cmd->flags & CMD_NOSCRIPT)) { luaPushError(lua, "This Redis command is not allowed from scripts"); goto cleanup; } /* This check is for EVAL_RO, EVALSHA_RO. We want to allow only read only commands */ - if ((server.lua_caller->cmd->proc == evalRoCommand || - server.lua_caller->cmd->proc == evalShaRoCommand) && + if ((server.script_caller->cmd->proc == evalRoCommand || + server.script_caller->cmd->proc == evalShaRoCommand) && (cmd->flags & CMD_WRITE)) { luaPushError(lua, "Write commands are not allowed from read-only scripts"); @@ -828,13 +828,13 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) { * of this script. */ if (cmd->flags & CMD_WRITE) { int deny_write_type = writeCommandsDeniedByDiskError(); - if (server.lua_random_dirty && !server.lua_replicate_commands) { + if (lctx.lua_random_dirty && !lctx.lua_replicate_commands) { luaPushError(lua, "Write commands not allowed after non deterministic commands. Call redis.replicate_commands() at the start of your script in order to switch to single commands replication mode."); goto cleanup; } else if (server.masterhost && server.repl_slave_ro && - server.lua_caller->id != CLIENT_ID_AOF && - !(server.lua_caller->flags & CLIENT_MASTER)) + server.script_caller->id != CLIENT_ID_AOF && + !(server.script_caller->flags & CLIENT_MASTER)) { luaPushError(lua, shared.roslaveerr->ptr); goto cleanup; @@ -857,29 +857,29 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) { * first write in the context of this script, otherwise we can't stop * in the middle. */ if (server.maxmemory && /* Maxmemory is actually enabled. */ - server.lua_caller->id != CLIENT_ID_AOF && /* Don't care about mem if loading from AOF. */ + server.script_caller->id != CLIENT_ID_AOF && /* Don't care about mem if loading from AOF. */ !server.masterhost && /* Slave must execute the script. */ - server.lua_write_dirty == 0 && /* Script had no side effects so far. */ - server.lua_oom && /* Detected OOM when script start. */ + lctx.lua_write_dirty == 0 && /* Script had no side effects so far. */ + server.script_oom && /* Detected OOM when script start. */ (cmd->flags & CMD_DENYOOM)) { luaPushError(lua, shared.oomerr->ptr); goto cleanup; } - if (cmd->flags & CMD_RANDOM) server.lua_random_dirty = 1; - if (cmd->flags & CMD_WRITE) server.lua_write_dirty = 1; + if (cmd->flags & CMD_RANDOM) lctx.lua_random_dirty = 1; + if (cmd->flags & CMD_WRITE) lctx.lua_write_dirty = 1; /* If this is a Redis Cluster node, we need to make sure Lua is not * trying to access non-local keys, with the exception of commands * received from our master or when loading the AOF back in memory. */ - if (server.cluster_enabled && server.lua_caller->id != CLIENT_ID_AOF && - !(server.lua_caller->flags & CLIENT_MASTER)) + if (server.cluster_enabled && server.script_caller->id != CLIENT_ID_AOF && + !(server.script_caller->flags & CLIENT_MASTER)) { int error_code; /* Duplicate relevant flags in the lua client. */ c->flags &= ~(CLIENT_READONLY|CLIENT_ASKING); - c->flags |= server.lua_caller->flags & (CLIENT_READONLY|CLIENT_ASKING); + c->flags |= server.script_caller->flags & (CLIENT_READONLY|CLIENT_ASKING); if (getNodeByQuery(c,c->cmd,c->argv,c->argc,NULL,&error_code) != server.cluster->myself) { @@ -904,14 +904,14 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) { /* If we are using single commands replication, we need to wrap what * we propagate into a MULTI/EXEC block, so that it will be atomic like * a Lua script in the context of AOF and slaves. */ - if (server.lua_replicate_commands && - !server.lua_multi_emitted && - !(server.lua_caller->flags & CLIENT_MULTI) && - server.lua_write_dirty && - server.lua_repl != PROPAGATE_NONE) + if (lctx.lua_replicate_commands && + !lctx.lua_multi_emitted && + !(server.script_caller->flags & CLIENT_MULTI) && + lctx.lua_write_dirty && + lctx.lua_repl != PROPAGATE_NONE) { - execCommandPropagateMulti(server.lua_caller->db->id); - server.lua_multi_emitted = 1; + execCommandPropagateMulti(server.script_caller->db->id); + lctx.lua_multi_emitted = 1; /* Now we are in the MULTI context, the lua_client should be * flag as CLIENT_MULTI. */ c->flags |= CLIENT_MULTI; @@ -919,11 +919,11 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) { /* Run the command */ int call_flags = CMD_CALL_SLOWLOG | CMD_CALL_STATS; - if (server.lua_replicate_commands) { + if (lctx.lua_replicate_commands) { /* Set flags according to redis.set_repl() settings. */ - if (server.lua_repl & PROPAGATE_AOF) + if (lctx.lua_repl & PROPAGATE_AOF) call_flags |= CMD_CALL_PROPAGATE_AOF; - if (server.lua_repl & PROPAGATE_REPL) + if (lctx.lua_repl & PROPAGATE_REPL) call_flags |= CMD_CALL_PROPAGATE_REPL; } call(c,call_flags); @@ -953,13 +953,13 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) { redisProtocolToLuaType(lua,reply); /* If the debugger is active, log the reply from Redis. */ - if (ldb.active && ldb.step) + if (ldbIsEnabled()) ldbLogRedisReply(reply); /* Sort the output array if needed, assuming it is a non-null multi bulk * reply as expected. */ if ((cmd->flags & CMD_SORT_FOR_SCRIPT) && - (server.lua_replicate_commands == 0) && + (lctx.lua_replicate_commands == 0) && (reply[0] == '*' && reply[1] != '-')) { luaSortArray(lua); } @@ -1076,7 +1076,7 @@ int luaRedisSetReplCommand(lua_State *lua) { int argc = lua_gettop(lua); int flags; - if (server.lua_replicate_commands == 0) { + if (lctx.lua_replicate_commands == 0) { lua_pushstring(lua, "You can set the replication behavior only after turning on single commands replication with redis.replicate_commands()."); return lua_error(lua); } else if (argc != 1) { @@ -1089,7 +1089,7 @@ int luaRedisSetReplCommand(lua_State *lua) { lua_pushstring(lua, "Invalid replication flags. Use REPL_AOF, REPL_REPLICA, REPL_ALL or REPL_NONE."); return lua_error(lua); } - server.lua_repl = flags; + lctx.lua_repl = flags; return 0; } @@ -1145,7 +1145,7 @@ int luaSetResp(lua_State *lua) { return lua_error(lua); } - server.lua_client->resp = resp; + lctx.lua_client->resp = resp; return 0; } @@ -1385,29 +1385,29 @@ int redis_math_randomseed (lua_State *L) { /* This is the Lua script "count" hook that we use to detect scripts timeout. */ void luaMaskCountHook(lua_State *lua, lua_Debug *ar) { - long long elapsed = elapsedMs(server.lua_time_start); + long long elapsed = elapsedMs(lctx.lua_time_start); UNUSED(ar); UNUSED(lua); /* Set the timeout condition if not already set and the maximum * execution time was reached. */ - if (elapsed >= server.lua_time_limit && server.lua_timedout == 0) { + if (elapsed >= server.script_time_limit && server.script_timedout == 0) { serverLog(LL_WARNING, "Lua slow script detected: still in execution after %lld milliseconds. " "You can try killing the script using the SCRIPT KILL command. " "Script SHA1 is: %s", - elapsed, server.lua_cur_script); - server.lua_timedout = 1; + elapsed, lctx.lua_cur_script); + server.script_timedout = 1; blockingOperationStarts(); /* Once the script timeouts we reenter the event loop to permit others * to call SCRIPT KILL or SHUTDOWN NOSAVE if needed. For this reason * we need to mask the client executing the script from the event loop. * If we don't do that the client may disconnect and could no longer be * here when the EVAL command will return. */ - protectClient(server.lua_caller); + protectClient(server.script_caller); } - if (server.lua_timedout) processEventsWhileBlocked(); - if (server.lua_kill) { + if (server.script_timedout) processEventsWhileBlocked(); + if (lctx.lua_kill) { serverLog(LL_WARNING,"Lua script killed by user with SCRIPT KILL."); /* diff --git a/src/script_lua.h b/src/script_lua.h new file mode 100644 index 000000000..f9cf6f18a --- /dev/null +++ b/src/script_lua.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2009-2021, Redis Labs 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_LUA_H_ +#define __SCRIPT_LUA_H_ + +#include "server.h" +#include "script.h" +#include +#include +#include + +typedef struct luaCtx { + lua_State *lua; /* The Lua interpreter. We use just one for all clients */ + client *lua_client; /* The "fake client" to query Redis from Lua */ + char *lua_cur_script; /* SHA1 of the script currently running, or NULL */ + dict *lua_scripts; /* A dictionary of SHA1 -> Lua scripts */ + unsigned long long lua_scripts_mem; /* Cached scripts' memory + oh */ + int lua_replicate_commands; /* True if we are doing single commands repl. */ + int lua_write_dirty; + int lua_random_dirty; + int lua_multi_emitted; + int lua_repl; + int lua_kill; + monotime lua_time_start; /* monotonic timer to detect timed-out script */ + mstime_t lua_time_snapshot; /* Snapshot of mstime when script is started */ +} luaCtx; + +extern luaCtx lctx; + +void luaEngineRegisterRedisAPI(lua_State* lua); +void scriptingEnableGlobalsProtection(lua_State *lua); +void luaSetGlobalArray(lua_State *lua, char *var, robj **elev, int elec); +void luaMaskCountHook(lua_State *lua, lua_Debug *ar); +void luaReplyToRedisReply(client *c, lua_State *lua); + + + +#endif /* __SCRIPT_LUA_H_ */ diff --git a/src/server.c b/src/server.c index 05ab6b93a..8d682c207 100644 --- a/src/server.c +++ b/src/server.c @@ -2994,7 +2994,7 @@ void cronUpdateMemoryStats() { /* LUA memory isn't part of zmalloc_used, but it is part of the process RSS, * so we must deduct it in order to be able to calculate correct * "allocator fragmentation" ratio */ - size_t lua_memory = lua_gc(server.lua,LUA_GCCOUNT,0)*1024LL; + size_t lua_memory = evalMemory(); server.cron_malloc_stats.allocator_resident = server.cron_malloc_stats.process_rss - lua_memory; } if (!server.cron_malloc_stats.allocator_active) @@ -4247,7 +4247,7 @@ void initServer(void) { server.pubsub_channels = dictCreate(&keylistDictType); server.pubsub_patterns = dictCreate(&keylistDictType); server.cronloops = 0; - server.in_eval = 0; + server.in_script = 0; server.in_exec = 0; server.propagate_in_transaction = 0; server.client_pause_in_transaction = 0; @@ -4937,11 +4937,11 @@ void call(client *c, int flags) { /* If the caller is Lua, we want to force the EVAL caller to propagate * the script if the command flag or client flag are forcing the * propagation. */ - if (c->flags & CLIENT_LUA && server.lua_caller) { + if (c->flags & CLIENT_LUA && server.script_caller) { if (c->flags & CLIENT_FORCE_REPL) - server.lua_caller->flags |= CLIENT_FORCE_REPL; + server.script_caller->flags |= CLIENT_FORCE_REPL; if (c->flags & CLIENT_FORCE_AOF) - server.lua_caller->flags |= CLIENT_FORCE_AOF; + server.script_caller->flags |= CLIENT_FORCE_AOF; } /* Note: the code below uses the real command that was executed @@ -5070,8 +5070,8 @@ void call(client *c, int flags) { /* If the client has keys tracking enabled for client side caching, * make sure to remember the keys it fetched via this command. */ if (c->cmd->flags & CMD_READONLY) { - client *caller = (c->flags & CLIENT_LUA && server.lua_caller) ? - server.lua_caller : c; + client *caller = (c->flags & CLIENT_LUA && server.script_caller) ? + server.script_caller : c; if (caller->flags & CLIENT_TRACKING && !(caller->flags & CLIENT_TRACKING_BCAST)) { @@ -5172,14 +5172,14 @@ void populateCommandMovableKeys(struct redisCommand *cmd) { * other operations can be performed by the caller. Otherwise * if C_ERR is returned the client was destroyed (i.e. after QUIT). */ int processCommand(client *c) { - if (!server.lua_timedout) { + if (!server.script_timedout) { /* Both EXEC and EVAL call call() directly so there should be * no way in_exec or in_eval or propagate_in_transaction is 1. * That is unless lua_timedout, in which case client may run * some commands. */ serverAssert(!server.propagate_in_transaction); serverAssert(!server.in_exec); - serverAssert(!server.in_eval); + serverAssert(!server.in_script); } moduleCallCommandFilters(c); @@ -5274,7 +5274,7 @@ int processCommand(client *c) { if (server.cluster_enabled && !(c->flags & CLIENT_MASTER) && !(c->flags & CLIENT_LUA && - server.lua_caller->flags & CLIENT_MASTER) && + server.script_caller->flags & CLIENT_MASTER) && !(!c->cmd->movablekeys && c->cmd->key_specs_num == 0 && c->cmd->proc != execCommand)) { @@ -5309,7 +5309,7 @@ int processCommand(client *c) { * the event loop since there is a busy Lua script running in timeout * condition, to avoid mixing the propagation of scripts with the * propagation of DELs due to eviction. */ - if (server.maxmemory && !server.lua_timedout) { + if (server.maxmemory && !server.script_timedout) { int out_of_memory = (performEvictions() == EVICT_FAIL); /* performEvictions may evict keys, so we need flush pending tracking @@ -5345,7 +5345,7 @@ int processCommand(client *c) { * until first write within script, memory used by lua stack and * arguments might interfere. */ if (c->cmd->proc == evalCommand || c->cmd->proc == evalShaCommand) { - server.lua_oom = out_of_memory; + server.script_oom = out_of_memory; } } @@ -5432,7 +5432,7 @@ int processCommand(client *c) { * the MULTI plus a few initial commands refused, then the timeout * condition resolves, and the bottom-half of the transaction gets * executed, see Github PR #7022. */ - if (server.lua_timedout && + if (server.script_timedout && c->cmd->proc != authCommand && c->cmd->proc != helloCommand && c->cmd->proc != replconfCommand && @@ -6286,7 +6286,7 @@ sds genRedisInfoString(const char *section) { size_t zmalloc_used = zmalloc_used_memory(); size_t total_system_mem = server.system_memory_size; const char *evict_policy = evictPolicyToString(); - long long memory_lua = server.lua ? (long long)lua_gc(server.lua,LUA_GCCOUNT,0)*1024 : 0; + long long memory_lua = evalMemory(); struct redisMemOverhead *mh = getMemoryOverheadData(); /* Peak memory is updated from time to time by serverCron() so it @@ -6369,7 +6369,7 @@ sds genRedisInfoString(const char *section) { used_memory_lua_hmem, (long long) mh->lua_caches, used_memory_scripts_hmem, - dictSize(server.lua_scripts), + dictSize(evalScriptsDict()), server.maxmemory, maxmemory_hmem, evict_policy, diff --git a/src/server.h b/src/server.h index c06b23631..959b0e2d1 100644 --- a/src/server.h +++ b/src/server.h @@ -1334,7 +1334,7 @@ struct redisServer { int sentinel_mode; /* True if this instance is a Sentinel. */ size_t initial_memory_usage; /* Bytes used after initialization. */ int always_show_logo; /* Show logo even for non-stdout logging. */ - int in_eval; /* Are we inside EVAL? */ + int in_script; /* Are we inside EVAL? */ int in_exec; /* Are we inside EXEC? */ int propagate_in_transaction; /* Make sure we don't propagate nested MULTI/EXEC */ char *ignore_warnings; /* Config: warnings that should be ignored. */ @@ -1719,28 +1719,13 @@ struct redisServer { is down? */ int cluster_config_file_lock_fd; /* cluster config fd, will be flock */ /* Scripting */ - lua_State *lua; /* The Lua interpreter. We use just one for all clients */ - client *lua_client; /* The "fake client" to query Redis from Lua */ - client *lua_caller; /* The client running EVAL right now, or NULL */ - char* lua_cur_script; /* SHA1 of the script currently running, or NULL */ - dict *lua_scripts; /* A dictionary of SHA1 -> Lua scripts */ - unsigned long long lua_scripts_mem; /* Cached scripts' memory + oh */ - mstime_t lua_time_limit; /* Script timeout in milliseconds */ - monotime lua_time_start; /* monotonic timer to detect timed-out script */ - mstime_t lua_time_snapshot; /* Snapshot of mstime when script is started */ - int lua_write_dirty; /* True if a write command was called during the - execution of the current script. */ - int lua_random_dirty; /* True if a random command was called during the - execution of the current script. */ - int lua_replicate_commands; /* True if we are doing single commands repl. */ - int lua_multi_emitted;/* True if we already propagated MULTI. */ - int lua_repl; /* Script replication flags for redis.set_repl(). */ - int lua_timedout; /* True if we reached the time limit for script - execution. */ - int lua_kill; /* Kill the script if true. */ + client *script_caller; /* The client running script right now, or NULL */ + mstime_t script_time_limit; /* Script timeout in milliseconds */ + int script_timedout; /* True if we reached the time limit for script + execution. */ int lua_always_replicate_commands; /* Default replication type. */ - int lua_oom; /* OOM detected when script start? */ - int lua_disable_deny_script; /* Allow running commands marked "no-script" inside a script. */ + int script_oom; /* OOM detected when script start */ + int script_disable_deny_script; /* Allow running commands marked "no-script" inside a script. */ /* Lazy free */ int lazyfree_lazy_eviction; int lazyfree_lazy_expire; @@ -2722,8 +2707,16 @@ void scriptingInit(int setup); int ldbRemoveChild(pid_t pid); void ldbKillForkedSessions(void); int ldbPendingChildren(void); -sds luaCreateFunction(client *c, lua_State *lua, robj *body); +sds luaCreateFunction(client *c, robj *body); void freeLuaScriptsAsync(dict *lua_scripts); +int ldbIsEnabled(); +void ldbLog(sds entry); +void ldbLogRedisReply(char *reply); +void sha1hex(char *digest, char *script, size_t len); +unsigned long evalMemory(); +dict* evalScriptsDict(); +unsigned long evalScriptsMemory(); +mstime_t evalTimeSnapshot(); /* Blocked clients */ void processUnblockedClients(void);