From 9f772cc23744804fc4b5da2aa7985c4c512abf55 Mon Sep 17 00:00:00 2001
From: antirez <antirez@gmail.com>
Date: Tue, 27 Sep 2011 15:30:31 +0200
Subject: [PATCH] Return errors if a write command is called inside a Lua
 script after a random command was called. See
 https://github.com/antirez/redis/issues/95 for more information.

---
 src/redis.h     |  2 ++
 src/scripting.c | 18 ++++++++++++++++++
 2 files changed, 20 insertions(+)

diff --git a/src/redis.h b/src/redis.h
index 03260264..ec982ee9 100644
--- a/src/redis.h
+++ b/src/redis.h
@@ -623,6 +623,8 @@ struct redisServer {
     dict *lua_scripts; /* A dictionary of SHA1 -> Lua scripts */
     long long lua_time_limit;
     long long lua_time_start;
+    int lua_random_dirty; /* True if a random command was called during the
+                             exection of the current script. */
 };
 
 typedef struct pubsubPattern {
diff --git a/src/scripting.c b/src/scripting.c
index 0c18d916..f62e5c29 100644
--- a/src/scripting.c
+++ b/src/scripting.c
@@ -180,6 +180,14 @@ int luaRedisCommand(lua_State *lua) {
         goto cleanup;
     }
 
+    if (cmd->flags & REDIS_CMD_WRITE && server.lua_random_dirty) {
+        luaPushError(lua,
+            "Write commands not allowed after non deterministic commands");
+        goto cleanup;
+    }
+
+    if (cmd->flags & REDIS_CMD_RANDOM) server.lua_random_dirty = 1;
+
     /* Run the command */
     cmd->proc(c);
 
@@ -425,6 +433,16 @@ void evalGenericCommand(redisClient *c, int evalsha) {
      * not affected by external state. */
     redisSrand48(0);
 
+    /* We set this flag to zero to remember that so far no random command
+     * was called. This way we can allow the user to call commands like
+     * SRANDMEMBER or RANDOMKEY from Lua scripts as far as no write command
+     * is called (otherwise the replication and AOF would end with non
+     * deterministic sequences).
+     *
+     * Thanks to this flag we'll raise an error every time a write command
+     * is called after a random command was used. */
+    server.lua_random_dirty = 0;
+
     /* Get the number of arguments that are keys */
     if (getLongLongFromObjectOrReply(c,c->argv[2],&numkeys,NULL) != REDIS_OK)
         return;
-- 
2.47.2