Replicate EVALSHA as EVAL taking a dictionary of sha1 -> script source code.
This commit is contained in:
parent
70ecddc9f4
commit
4dd444bb4a
@ -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);
|
||||
}
|
||||
}
|
||||
|
17
src/object.c
17
src/object.c
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user