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.
This commit is contained in:
parent
22aab1ce94
commit
e0cd580aef
@ -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;
|
||||
|
@ -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),
|
||||
|
4
src/db.c
4
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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
171
src/eval.c
171
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();
|
||||
}
|
||||
}
|
||||
|
@ -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. */
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
@ -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++;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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(),"<error> %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("<redis>");
|
||||
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.");
|
||||
|
||||
/*
|
||||
|
65
src/script_lua.h
Normal file
65
src/script_lua.h
Normal file
@ -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 <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
|
||||
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_ */
|
30
src/server.c
30
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,
|
||||
|
39
src/server.h
39
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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user