]> git.saurik.com Git - redis.git/commitdiff
Merge branch 'unstable' of github.com:antirez/redis into unstable
authorantirez <antirez@gmail.com>
Wed, 13 Jul 2011 13:38:33 +0000 (15:38 +0200)
committerantirez <antirez@gmail.com>
Wed, 13 Jul 2011 13:38:33 +0000 (15:38 +0200)
TODO
src/SCRIPTING.txt [deleted file]
src/networking.c
src/object.c
src/redis.c
src/redis.h
src/scripting.c

diff --git a/TODO b/TODO
index dd6ba28bd6f149b7644849833df8b6a02f759fb7..d45b35047becca1f9445bfb73692a67e72fdf582 100644 (file)
--- a/TODO
+++ b/TODO
@@ -28,6 +28,14 @@ CLUSTER
 * Allow redis-trib to create a cluster-wide snapshot (using SYNC).
 * Allow redis-trib to restore a cluster-wide snapshot (implement UPLOAD?).
 
+SCRIPTING
+=========
+
+* MULTI/EXEC/...: should we do more than simply ignoring it?
+* Prevent Lua from calling itself with redis("eval",...)
+* SCRIPT FLUSH or alike to start a fresh interpreter?
+* http://redis.io/topics/sponsors
+
 APPEND ONLY FILE
 ================
 
diff --git a/src/SCRIPTING.txt b/src/SCRIPTING.txt
deleted file mode 100644 (file)
index 31dd6a3..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-Documentation:
-
-    http://antirez.com/post/scripting-branch-released.html
-
-TODO:
-
-    MULTI/EXEC/...: should we do more than simply ignoring it?
-
-    Prevent Lua from calling itself with redis("eval",...)
-
-    Implement log() function, and define REDIS_WARNING, REDIS_NOTICE, ...
-
-    SCRIPT FLUSH or alike to start a fresh interpreter.
-
-    Max exection time for scripts, even pretty high, but in order to
-    prevent that an instance gets completely freezed by error.
-    Hint: Use a debug hook for this (lua_sethook() with LUA_MASKCOUNT).
-
-Blog post TODO:
-
-    Explain that for now this is just an experiemnt and there is no way to
-    tell if this will ever enter Redis stable, but that since it is pretty
-    self contained it is trivial to rebase with the unstable branch.
-
-    Explain how replication / AOF mix with scripting and why it is this way,
-    and that scripts should be written to don't depend from timing or other
-    external events if you want to use AOF / replication.
-
-    Show how fast it is!
-
-    Mention that dynamically generated scripts are not a good idea.
index a4eee4643f33b926f2ebd3ed81e536c7f54392d3..629267d1cad2d025fe958407569c1a88043a810c 100644 (file)
@@ -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);
+    }
+}
index ce13429af94df159cf51307d721f8da52e152550..c1df4d1d2ad8dc91a904223c384ba5aa8bdf3959 100644 (file)
@@ -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);
index 7e9c6fd5ce72b177d146286642eae5fb984cfb19..1d7501f9376edcb462a9f48a4c3a665b2152b212 100644 (file)
@@ -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 */
index 1d094c1d7bfaf9fef4765be6d173d46008087af7..1a45cc8cd52260d525a8388fa52f36adcf06eb07 100644 (file)
@@ -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);
index 469d78708caf9cef1824632fbb067c856911dd8c..794cba5e39580141512af8f1e05e95a7cce4fb8f 100644 (file)
@@ -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) {