# "no" that is the safest pick from the point of view of durability.
no-appendfsync-on-rewrite no
+################################ 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
+
#################################### DISK STORE ###############################
# When disk store is active Redis works as an on-disk database, where memory
} else if (!strcasecmp(argv[0],"cluster-config-file") && argc == 2) {
zfree(server.cluster.configfile);
server.cluster.configfile = zstrdup(argv[1]);
+ } else if (!strcasecmp(argv[0],"lua-time-limit") && argc == 2) {
+ server.lua_time_limit = strtoll(argv[1],NULL,10);
} else {
err = "Bad directive or wrong number of arguments"; goto loaderr;
}
} else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-value")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.zset_max_ziplist_value = ll;
+ } else if (!strcasecmp(c->argv[2]->ptr,"lua-time-limit")) {
+ if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
+ server.lua_time_limit = ll;
} else {
addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
(char*)c->argv[2]->ptr);
addReplyBulkLongLong(c,server.zset_max_ziplist_value);
matches++;
}
+ if (stringmatch(pattern,"lua-time-limit",0)) {
+ addReplyBulkCString(c,"lua-time-limit");
+ addReplyBulkLongLong(c,server.lua_time_limit);
+ matches++;
+ }
setDeferredMultiBulkLength(c,replylen,matches*2);
}
server.cache_flush_delay = 0;
server.cluster_enabled = 0;
server.cluster.configfile = zstrdup("nodes.conf");
+ server.lua_time_limit = REDIS_LUA_TIME_LIMIT;
updateLRUClock();
resetServerSaveParams();
#define REDIS_BGSAVE_THREAD_DONE_OK 2
#define REDIS_BGSAVE_THREAD_DONE_ERR 3
+/* Scripting */
+#define REDIS_LUA_TIME_LIMIT 60000 /* milliseconds */
+
/* We can print the stacktrace, so our assert is defined this way: */
#define redisAssert(_e) ((_e)?(void)0 : (_redisAssert(#_e,__FILE__,__LINE__),_exit(1)))
#define redisPanic(_e) _redisPanic(#_e,__FILE__,__LINE__),_exit(1)
/* Scripting */
lua_State *lua;
redisClient *lua_client;
+ long long lua_time_limit;
+ long long lua_time_start;
};
typedef struct pubsubPattern {
return 1;
}
+void luaMaskCountHook(lua_State *lua, lua_Debug *ar) {
+ long long elapsed;
+ REDIS_NOTUSED(ar);
+
+ if (server.lua_time_limit <= 0) return;
+ elapsed = (ustime()/1000) - server.lua_time_start;
+ if (elapsed >= server.lua_time_limit) {
+ lua_pushstring(lua,"Script aborted for max execution time...");
+ lua_error(lua);
+ redisLog(REDIS_NOTICE,"Lua script aborted for max execution time after %lld milliseconds of running time",elapsed);
+ }
+}
+
void scriptingInit(void) {
lua_State *lua = lua_open();
luaL_openlibs(lua);
server.lua_client = createClient(-1);
server.lua_client->flags |= REDIS_LUA_CLIENT;
+ /* Set an hook in order to be able to stop the script execution if it
+ * is running for too much time. */
+ lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,10000);
+
server.lua = lua;
}
/* At this point whatever this script was never seen before or if it was
* already defined, we can call it. We have zero arguments and expect
* a single return value. */
+ server.lua_time_start = ustime()/1000;
if (lua_pcall(lua,0,1,0)) {
selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
addReplyErrorFormat(c,"Error running script (call to %s): %s\n",