提交 4dd444bb 编写于 作者: A antirez

Replicate EVALSHA as EVAL taking a dictionary of sha1 -> script source code.

上级 70ecddc9
......@@ -941,6 +941,9 @@ void clientCommand(redisClient *c) {
}
}
/* Rewrite the command vector of the client. All the new objects ref count
* is incremented. The old command vector is freed, and the old objects
* ref count is decremented. */
void rewriteClientCommandVector(redisClient *c, int argc, ...) {
va_list ap;
int j;
......@@ -967,3 +970,21 @@ void rewriteClientCommandVector(redisClient *c, int argc, ...) {
redisAssert(c->cmd != NULL);
va_end(ap);
}
/* Rewrite a single item in the command vector.
* The new val ref count is incremented, and the old decremented. */
void rewriteClientCommandArgument(redisClient *c, int i, robj *newval) {
robj *oldval;
redisAssert(i < c->argc);
oldval = c->argv[i];
c->argv[i] = newval;
incrRefCount(newval);
decrRefCount(oldval);
/* If this is the command name make sure to fix c->cmd. */
if (i == 0) {
c->cmd = lookupCommand(c->argv[0]->ptr);
redisAssert(c->cmd != NULL);
}
}
......@@ -192,6 +192,23 @@ void decrRefCount(void *obj) {
}
}
/* This function set the ref count to zero without freeing the object.
* It is useful in order to pass a new object to functions incrementing
* the ref count of the received object. Example:
*
* functionThatWillIncrementRefCount(resetRefCount(CreateObject(...)));
*
* Otherwise you need to resort to the less elegant pattern:
*
* *obj = createObject(...);
* functionThatWillIncrementRefCount(obj);
* decrRefCount(obj);
*/
robj *resetRefCount(robj *obj) {
obj->refcount = 0;
return obj;
}
int checkType(redisClient *c, robj *o, int type) {
if (o->type != type) {
addReply(c,shared.wrongtypeerr);
......
......@@ -383,7 +383,7 @@ unsigned int dictEncObjHash(const void *key) {
}
}
/* Sets type and diskstore negative caching hash table */
/* Sets type hash table */
dictType setDictType = {
dictEncObjHash, /* hash function */
NULL, /* key dup */
......
......@@ -646,8 +646,9 @@ struct redisServer {
int cluster_enabled;
clusterState cluster;
/* Scripting */
lua_State *lua;
redisClient *lua_client;
lua_State *lua; /* The Lua interpreter. We use just one for all clients */
redisClient *lua_client; /* The "fake client" to query Redis from Lua */
dict *lua_scripts; /* A dictionary of SHA1 -> Lua scripts */
long long lua_time_limit;
long long lua_time_start;
};
......@@ -742,6 +743,7 @@ extern struct sharedObjectsStruct shared;
extern dictType setDictType;
extern dictType zsetDictType;
extern dictType clusterNodesDictType;
extern dictType dbDictType;
extern double R_Zero, R_PosInf, R_NegInf, R_Nan;
dictType hashDictType;
......@@ -782,6 +784,7 @@ void *dupClientReplyValue(void *o);
void getClientsMaxBuffers(unsigned long *longest_output_list,
unsigned long *biggest_input_buffer);
void rewriteClientCommandVector(redisClient *c, int argc, ...);
void rewriteClientCommandArgument(redisClient *c, int i, robj *newval);
#ifdef __GNUC__
void addReplyErrorFormat(redisClient *c, const char *fmt, ...)
......@@ -821,6 +824,7 @@ void touchWatchedKeysOnFlush(int dbid);
/* Redis object implementation */
void decrRefCount(void *o);
void incrRefCount(robj *o);
robj *resetRefCount(robj *obj);
void freeStringObject(robj *o);
void freeListObject(robj *o);
void freeSetObject(robj *o);
......
......@@ -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);
......@@ -455,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
......@@ -490,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) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册