################################ LUA SCRIPTING ###############################
# Max execution time of a Lua script in milliseconds.
-# This prevents that a programming error generating an infinite loop will block
-# your server forever. Set it to 0 or a negative value for unlimited execution.
-lua-time-limit 60000
+#
+# If the maximum execution time is reached Redis will log that a script is
+# still in execution after the maxium allowed time and will start to
+# reply to queries with an error.
+#
+# The SHUTDOWN command will be available to shutdown the server without
+# violating the database consistency if the script entered an infinite loop.
+#
+# Set it to 0 or a negative value for unlimited execution without warnings.
+lua-time-limit 5000
################################ REDIS CLUSTER ###############################
#
"-NOSCRIPT No matching script. Please use EVAL.\r\n"));
shared.loadingerr = createObject(REDIS_STRING,sdsnew(
"-LOADING Redis is loading the dataset in memory\r\n"));
+ shared.slowscripterr = createObject(REDIS_STRING,sdsnew(
+ "-SLOWSCRIPT Redis is busy running a script. Please wait or stop the server with SHUTDOWN.\r\n"));
shared.space = createObject(REDIS_STRING,sdsnew(" "));
shared.colon = createObject(REDIS_STRING,sdsnew(":"));
shared.plus = createObject(REDIS_STRING,sdsnew("+"));
server.cluster.configfile = zstrdup("nodes.conf");
server.lua_time_limit = REDIS_LUA_TIME_LIMIT;
server.lua_client = NULL;
+ server.lua_timedout = 0;
updateLRUClock();
resetServerSaveParams();
return REDIS_OK;
}
+ /* Lua script too slow? */
+ if (server.lua_timedout && c->cmd->proc != shutdownCommand) {
+ addReply(c, shared.slowscripterr);
+ return REDIS_OK;
+ }
+
/* Exec the command */
if (c->flags & REDIS_MULTI &&
c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
robj *crlf, *ok, *err, *emptybulk, *czero, *cone, *cnegone, *pong, *space,
*colon, *nullbulk, *nullmultibulk, *queued,
*emptymultibulk, *wrongtypeerr, *nokeyerr, *syntaxerr, *sameobjecterr,
- *outofrangeerr, *noscripterr, *loadingerr, *plus,
+ *outofrangeerr, *noscripterr, *loadingerr, *slowscripterr, *plus,
*select0, *select1, *select2, *select3, *select4,
*select5, *select6, *select7, *select8, *select9,
*messagebulk, *pmessagebulk, *subscribebulk, *unsubscribebulk, *mbulk3,
long long lua_time_start;
int lua_random_dirty; /* True if a random command was called during the
exection of the current script. */
+ int lua_timedout; /* True if we reached the time limit for script
+ execution. */
};
typedef struct pubsubPattern {
void luaMaskCountHook(lua_State *lua, lua_Debug *ar) {
long long elapsed;
REDIS_NOTUSED(ar);
+ REDIS_NOTUSED(lua);
elapsed = (ustime()/1000) - server.lua_time_start;
- if (elapsed >= server.lua_time_limit) {
- redisLog(REDIS_NOTICE,"Lua script aborted for max execution time after %lld milliseconds of running time.",elapsed);
- lua_pushstring(lua,"Script aborted for max execution time.");
- lua_error(lua);
+ if (elapsed >= server.lua_time_limit && server.lua_timedout == 0) {
+ redisLog(REDIS_WARNING,"Lua slow script detected: still in execution after %lld milliseconds. You can shut down the server using the SHUTDOWN command.",elapsed);
+ server.lua_timedout = 1;
}
+ if (server.lua_timedout)
+ aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT);
}
void luaLoadLib(lua_State *lua, const char *libname, lua_CFunction luafunc) {
* is running for too much time.
* We set the hook only if the time limit is enabled as the hook will
* make the Lua script execution slower. */
- if (server.lua_time_limit > 0 && server.masterhost != NULL) {
+ if (server.lua_time_limit > 0 && server.masterhost == NULL) {
lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
server.lua_time_start = ustime()/1000;
} else {
* already defined, we can call it. We have zero arguments and expect
* a single return value. */
if (lua_pcall(lua,0,1,0)) {
+ server.lua_timedout = 0;
selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
addReplyErrorFormat(c,"Error running script (call to %s): %s\n",
funcname, lua_tostring(lua,-1));
lua_gc(lua,LUA_GCCOLLECT,0);
return;
}
+ server.lua_timedout = 0;
selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
luaReplyToRedisReply(c,lua);
lua_gc(lua,LUA_GCSTEP,1);