]> git.saurik.com Git - redis.git/commitdiff
If a Lua script executes for more time than the max time specified in the configurati...
authorantirez <antirez@gmail.com>
Thu, 27 Oct 2011 12:49:10 +0000 (14:49 +0200)
committerantirez <antirez@gmail.com>
Thu, 27 Oct 2011 12:49:10 +0000 (14:49 +0200)
redis.conf
src/redis.c
src/redis.h
src/scripting.c

index 18a5dd036ae0f832a168d3691e41a26adf4b4f3d..71d6f70b00ef2069c28af1a6844e064644534714 100644 (file)
@@ -316,9 +316,16 @@ auto-aof-rewrite-min-size 64mb
 ################################ LUA SCRIPTING  ###############################
 
 # Max execution time of a Lua script in milliseconds.
 ################################ 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  ###############################
 #
 
 ################################ REDIS CLUSTER  ###############################
 #
index f39d25b495ea00d966f2b7af7909daafaf0e8254..32644e33edbdbbae198306b61e6ea7fbf5ea15c0 100644 (file)
@@ -793,6 +793,8 @@ void createSharedObjects(void) {
         "-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"));
         "-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("+"));
     shared.space = createObject(REDIS_STRING,sdsnew(" "));
     shared.colon = createObject(REDIS_STRING,sdsnew(":"));
     shared.plus = createObject(REDIS_STRING,sdsnew("+"));
@@ -871,6 +873,7 @@ void initServerConfig() {
     server.cluster.configfile = zstrdup("nodes.conf");
     server.lua_time_limit = REDIS_LUA_TIME_LIMIT;
     server.lua_client = NULL;
     server.cluster.configfile = zstrdup("nodes.conf");
     server.lua_time_limit = REDIS_LUA_TIME_LIMIT;
     server.lua_client = NULL;
+    server.lua_timedout = 0;
 
     updateLRUClock();
     resetServerSaveParams();
 
     updateLRUClock();
     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 */
     if (c->flags & REDIS_MULTI &&
         c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
     /* Exec the command */
     if (c->flags & REDIS_MULTI &&
         c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
index d62bd80e56a4aec4a0942b1f0e343ab2b9387a62..1a9891ecaccd046dfc825bdaa97662ab40022dc8 100644 (file)
@@ -345,7 +345,7 @@ struct sharedObjectsStruct {
     robj *crlf, *ok, *err, *emptybulk, *czero, *cone, *cnegone, *pong, *space,
     *colon, *nullbulk, *nullmultibulk, *queued,
     *emptymultibulk, *wrongtypeerr, *nokeyerr, *syntaxerr, *sameobjecterr,
     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,
     *select0, *select1, *select2, *select3, *select4,
     *select5, *select6, *select7, *select8, *select9,
     *messagebulk, *pmessagebulk, *subscribebulk, *unsubscribebulk, *mbulk3,
@@ -639,6 +639,8 @@ struct redisServer {
     long long lua_time_start;
     int lua_random_dirty; /* True if a random command was called during the
                              exection of the current script. */
     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 {
 };
 
 typedef struct pubsubPattern {
index d1e85e26d6d429a82e817577de79fb30d4bd6c34..0b548873fd8fbac94e9b4b2f04a05f803db3af73 100644 (file)
@@ -273,13 +273,15 @@ int luaLogCommand(lua_State *lua) {
 void luaMaskCountHook(lua_State *lua, lua_Debug *ar) {
     long long elapsed;
     REDIS_NOTUSED(ar);
 void luaMaskCountHook(lua_State *lua, lua_Debug *ar) {
     long long elapsed;
     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) {
-        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) {
 }
 
 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.
      * We set the hook only if the time limit is enabled as the hook will
      * make the Lua script execution slower. */
      * 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 {
         lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
         server.lua_time_start = ustime()/1000;
     } else {
@@ -617,6 +619,7 @@ void evalGenericCommand(redisClient *c, int evalsha) {
      * already defined, we can call it. We have zero arguments and expect
      * a single return value. */
     if (lua_pcall(lua,0,1,0)) {
      * 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));
         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));
@@ -624,6 +627,7 @@ void evalGenericCommand(redisClient *c, int evalsha) {
         lua_gc(lua,LUA_GCCOLLECT,0);
         return;
     }
         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);
     selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
     luaReplyToRedisReply(c,lua);
     lua_gc(lua,LUA_GCSTEP,1);