X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/288f811feacdd6b26c2263e7b9fcfabaa051a1ec..b7c739b0ddb95ef274d141a6fe714fb34291324d:/src/scripting.c diff --git a/src/scripting.c b/src/scripting.c index 2f94f4d7..794cba5e 100644 --- a/src/scripting.c +++ b/src/scripting.c @@ -209,11 +209,11 @@ int luaLogCommand(lua_State *lua) { luaPushError(lua, "redis.log() requires two arguments or more."); return 1; } else if (!lua_isnumber(lua,-argc)) { - luaPushError(lua, "First argument must be a number."); + luaPushError(lua, "First argument must be a number (log level)."); return 1; } level = lua_tonumber(lua,-argc); - if (level < REDIS_DEBUG && level > REDIS_WARNING) { + if (level < REDIS_DEBUG || level > REDIS_WARNING) { luaPushError(lua, "Invalid debug level."); return 1; } @@ -251,6 +251,11 @@ void scriptingInit(void) { lua_State *lua = lua_open(); luaL_openlibs(lua); + /* 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(&dbDictType,NULL); + /* Register the redis commands table and fields */ lua_newtable(lua); @@ -332,8 +337,10 @@ void luaReplyToRedisReply(redisClient *c, lua_State *lua) { lua_gettable(lua,-2); t = lua_type(lua,-1); if (t == LUA_TSTRING) { - addReplySds(c,sdscatprintf(sdsempty(), - "-%s\r\n",(char*)lua_tostring(lua,-1))); + sds err = sdsnew(lua_tostring(lua,-1)); + sdsmapchars(err,"\r\n"," ",2); + addReplySds(c,sdscatprintf(sdsempty(),"-%s\r\n",err)); + sdsfree(err); lua_pop(lua,2); return; } @@ -343,8 +350,10 @@ void luaReplyToRedisReply(redisClient *c, lua_State *lua) { lua_gettable(lua,-2); t = lua_type(lua,-1); if (t == LUA_TSTRING) { - addReplySds(c,sdscatprintf(sdsempty(), - "+%s\r\n",(char*)lua_tostring(lua,-1))); + sds ok = sdsnew(lua_tostring(lua,-1)); + sdsmapchars(ok,"\r\n"," ",2); + addReplySds(c,sdscatprintf(sdsempty(),"+%s\r\n",ok)); + sdsfree(ok); lua_pop(lua,1); } else { void *replylen = addDeferredMultiBulkLength(c); @@ -451,6 +460,16 @@ void evalGenericCommand(redisClient *c, int evalsha) { return; } lua_getglobal(lua, funcname); + + /* 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, + sdsnewlen(funcname+2,40),c->argv[1]); + redisAssert(retval == DICT_OK); + incrRefCount(c->argv[1]); + } } /* Populate the argv and keys table accordingly to the arguments that @@ -486,6 +505,25 @@ void evalGenericCommand(redisClient *c, int evalsha) { selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */ luaReplyToRedisReply(c,lua); lua_gc(lua,LUA_GCSTEP,1); + + /* If we have slaves attached we want to replicate this command as + * EVAL instead of EVALSHA. We do this also in the AOF as currently there + * is no easy way to propagate a command in a different way in the AOF + * and in the replication link. + * + * IMPROVEMENT POSSIBLE: + * 1) Replicate this command as EVALSHA in the AOF. + * 2) Remember what slave already received a given script, and replicate + * the EVALSHA against this slaves when possible. + */ + if (evalsha) { + robj *script = dictFetchValue(server.lua_scripts,c->argv[1]->ptr); + + redisAssert(script != NULL); + rewriteClientCommandArgument(c,0, + resetRefCount(createStringObject("EVAL",4))); + rewriteClientCommandArgument(c,1,script); + } } void evalCommand(redisClient *c) {