If a Lua script executes for more time than the max time specified in the configuration Redis will log a warning, and will start accepting queries (re-entering the event loop), returning -SLOWSCRIPT error for all the commands but SHUTDOWN that remains callable.
This commit is contained in:
parent
bb48c5fa67
commit
115e3ff39e
13
redis.conf
13
redis.conf
@ -316,9 +316,16 @@ auto-aof-rewrite-min-size 64mb
|
|||||||
################################ LUA SCRIPTING ###############################
|
################################ LUA SCRIPTING ###############################
|
||||||
|
|
||||||
# Max execution time of a Lua script in milliseconds.
|
# 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.
|
# If the maximum execution time is reached Redis will log that a script is
|
||||||
lua-time-limit 60000
|
# 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 ###############################
|
################################ REDIS CLUSTER ###############################
|
||||||
#
|
#
|
||||||
|
@ -793,6 +793,8 @@ void createSharedObjects(void) {
|
|||||||
"-NOSCRIPT No matching script. Please use EVAL.\r\n"));
|
"-NOSCRIPT No matching script. Please use EVAL.\r\n"));
|
||||||
shared.loadingerr = createObject(REDIS_STRING,sdsnew(
|
shared.loadingerr = createObject(REDIS_STRING,sdsnew(
|
||||||
"-LOADING Redis is loading the dataset in memory\r\n"));
|
"-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.space = createObject(REDIS_STRING,sdsnew(" "));
|
||||||
shared.colon = createObject(REDIS_STRING,sdsnew(":"));
|
shared.colon = createObject(REDIS_STRING,sdsnew(":"));
|
||||||
shared.plus = createObject(REDIS_STRING,sdsnew("+"));
|
shared.plus = createObject(REDIS_STRING,sdsnew("+"));
|
||||||
@ -871,6 +873,7 @@ void initServerConfig() {
|
|||||||
server.cluster.configfile = zstrdup("nodes.conf");
|
server.cluster.configfile = zstrdup("nodes.conf");
|
||||||
server.lua_time_limit = REDIS_LUA_TIME_LIMIT;
|
server.lua_time_limit = REDIS_LUA_TIME_LIMIT;
|
||||||
server.lua_client = NULL;
|
server.lua_client = NULL;
|
||||||
|
server.lua_timedout = 0;
|
||||||
|
|
||||||
updateLRUClock();
|
updateLRUClock();
|
||||||
resetServerSaveParams();
|
resetServerSaveParams();
|
||||||
@ -1183,6 +1186,12 @@ int processCommand(redisClient *c) {
|
|||||||
return REDIS_OK;
|
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 */
|
/* Exec the command */
|
||||||
if (c->flags & REDIS_MULTI &&
|
if (c->flags & REDIS_MULTI &&
|
||||||
c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
|
c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
|
||||||
|
@ -345,7 +345,7 @@ struct sharedObjectsStruct {
|
|||||||
robj *crlf, *ok, *err, *emptybulk, *czero, *cone, *cnegone, *pong, *space,
|
robj *crlf, *ok, *err, *emptybulk, *czero, *cone, *cnegone, *pong, *space,
|
||||||
*colon, *nullbulk, *nullmultibulk, *queued,
|
*colon, *nullbulk, *nullmultibulk, *queued,
|
||||||
*emptymultibulk, *wrongtypeerr, *nokeyerr, *syntaxerr, *sameobjecterr,
|
*emptymultibulk, *wrongtypeerr, *nokeyerr, *syntaxerr, *sameobjecterr,
|
||||||
*outofrangeerr, *noscripterr, *loadingerr, *plus,
|
*outofrangeerr, *noscripterr, *loadingerr, *slowscripterr, *plus,
|
||||||
*select0, *select1, *select2, *select3, *select4,
|
*select0, *select1, *select2, *select3, *select4,
|
||||||
*select5, *select6, *select7, *select8, *select9,
|
*select5, *select6, *select7, *select8, *select9,
|
||||||
*messagebulk, *pmessagebulk, *subscribebulk, *unsubscribebulk, *mbulk3,
|
*messagebulk, *pmessagebulk, *subscribebulk, *unsubscribebulk, *mbulk3,
|
||||||
@ -639,6 +639,8 @@ struct redisServer {
|
|||||||
long long lua_time_start;
|
long long lua_time_start;
|
||||||
int lua_random_dirty; /* True if a random command was called during the
|
int lua_random_dirty; /* True if a random command was called during the
|
||||||
exection of the current script. */
|
exection of the current script. */
|
||||||
|
int lua_timedout; /* True if we reached the time limit for script
|
||||||
|
execution. */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct pubsubPattern {
|
typedef struct pubsubPattern {
|
||||||
|
@ -273,13 +273,15 @@ int luaLogCommand(lua_State *lua) {
|
|||||||
void luaMaskCountHook(lua_State *lua, lua_Debug *ar) {
|
void luaMaskCountHook(lua_State *lua, lua_Debug *ar) {
|
||||||
long long elapsed;
|
long long elapsed;
|
||||||
REDIS_NOTUSED(ar);
|
REDIS_NOTUSED(ar);
|
||||||
|
REDIS_NOTUSED(lua);
|
||||||
|
|
||||||
elapsed = (ustime()/1000) - server.lua_time_start;
|
elapsed = (ustime()/1000) - server.lua_time_start;
|
||||||
if (elapsed >= server.lua_time_limit) {
|
if (elapsed >= server.lua_time_limit && server.lua_timedout == 0) {
|
||||||
redisLog(REDIS_NOTICE,"Lua script aborted for max execution time after %lld milliseconds of running time.",elapsed);
|
redisLog(REDIS_WARNING,"Lua slow script detected: still in execution after %lld milliseconds. You can shut down the server using the SHUTDOWN command.",elapsed);
|
||||||
lua_pushstring(lua,"Script aborted for max execution time.");
|
server.lua_timedout = 1;
|
||||||
lua_error(lua);
|
|
||||||
}
|
}
|
||||||
|
if (server.lua_timedout)
|
||||||
|
aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void luaLoadLib(lua_State *lua, const char *libname, lua_CFunction luafunc) {
|
void luaLoadLib(lua_State *lua, const char *libname, lua_CFunction luafunc) {
|
||||||
@ -606,7 +608,7 @@ void evalGenericCommand(redisClient *c, int evalsha) {
|
|||||||
* is running for too much time.
|
* is running for too much time.
|
||||||
* We set the hook only if the time limit is enabled as the hook will
|
* We set the hook only if the time limit is enabled as the hook will
|
||||||
* make the Lua script execution slower. */
|
* 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);
|
lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
|
||||||
server.lua_time_start = ustime()/1000;
|
server.lua_time_start = ustime()/1000;
|
||||||
} else {
|
} else {
|
||||||
@ -617,6 +619,7 @@ void evalGenericCommand(redisClient *c, int evalsha) {
|
|||||||
* already defined, we can call it. We have zero arguments and expect
|
* already defined, we can call it. We have zero arguments and expect
|
||||||
* a single return value. */
|
* a single return value. */
|
||||||
if (lua_pcall(lua,0,1,0)) {
|
if (lua_pcall(lua,0,1,0)) {
|
||||||
|
server.lua_timedout = 0;
|
||||||
selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
|
selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
|
||||||
addReplyErrorFormat(c,"Error running script (call to %s): %s\n",
|
addReplyErrorFormat(c,"Error running script (call to %s): %s\n",
|
||||||
funcname, lua_tostring(lua,-1));
|
funcname, lua_tostring(lua,-1));
|
||||||
@ -624,6 +627,7 @@ void evalGenericCommand(redisClient *c, int evalsha) {
|
|||||||
lua_gc(lua,LUA_GCCOLLECT,0);
|
lua_gc(lua,LUA_GCCOLLECT,0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
server.lua_timedout = 0;
|
||||||
selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
|
selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
|
||||||
luaReplyToRedisReply(c,lua);
|
luaReplyToRedisReply(c,lua);
|
||||||
lua_gc(lua,LUA_GCSTEP,1);
|
lua_gc(lua,LUA_GCSTEP,1);
|
||||||
|
Loading…
Reference in New Issue
Block a user