Преглед на файлове

Add the ability to mark specific commands or scripts for EVAL as read-only.

As suggested by @Seldaek in ISSUE #21, it would be desiderable to have the
ability to mark certain scripts for EVAL (and EVALSHA) as read-only to avoid
switching to the master server when this is not really needed.

When marking a script as read-only, only its SHA1 hash is saved to use less
memory and to make this work transparently for both EVAL and EVALSHA.
Daniele Alessandri преди 13 години
родител
ревизия
5148ce16c6
променени са 1 файла, в които са добавени 77 реда и са изтрити 21 реда
  1. 77 21
      lib/Predis/Network/PredisReplication.php

+ 77 - 21
lib/Predis/Network/PredisReplication.php

@@ -23,6 +23,7 @@ use Predis\Commands\ICommand;
 class PredisReplication implements IConnectionReplication
 {
     private $readonly = array();
+    private $readonlySHA1 = array();
     private $current = null;
     private $master = null;
     private $slaves = array();
@@ -35,26 +36,6 @@ class PredisReplication implements IConnectionReplication
         $this->readonly = $this->getReadOnlyOperations();
     }
 
-    /**
-     * Returns if the specified command performs a read-only operation
-     * against a key stored on Redis.
-     *
-     * @param ICommand $command Instance of Redis command.
-     * @return Boolean
-     */
-    protected function isReadOperation(ICommand $command)
-    {
-        if (isset($this->readonly[$id = $command->getId()])) {
-            if (true === $readonly = $this->readonly[$id]) {
-                return true;
-            }
-
-            return $readonly($command);
-        }
-
-        return false;
-    }
-
     /**
      * Checks if one master and at least one slave have been defined.
      */
@@ -255,7 +236,82 @@ class PredisReplication implements IConnectionReplication
     }
 
     /**
-     * Returns a list of commands that perform read-only operations.
+     * Returns if the specified command performs a read-only operation
+     * against a key stored on Redis.
+     *
+     * @param ICommand $command Instance of Redis command.
+     * @return Boolean
+     */
+    protected function isReadOperation(ICommand $command)
+    {
+        if (isset($this->readonly[$id = $command->getId()])) {
+            if (true === $readonly = $this->readonly[$id]) {
+                return true;
+            }
+
+            return $readonly($command);
+        }
+
+        if (($eval = $id === 'EVAL') || $id === 'EVALSHA') {
+            $sha1 = $eval ? sha1($command->getArgument(0)) : $command->getArgument(0);
+
+            if (isset($this->readonlySHA1[$sha1])) {
+                if (true === $readonly = $this->readonlySHA1[$sha1]) {
+                    return true;
+                }
+
+                return $readonly($command);
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Marks a command as a read-only operation. When the behaviour of a
+     * command can be decided only at runtime depending on its arguments,
+     * a callable object can be provided to dinamically check if the passed
+     * instance of a command performs write operations or not.
+     *
+     * @param string $commandID ID of the command.
+     * @param mixed $readonly A boolean or a callable object.
+     */
+    public function setCommandReadOnly($commandID, $readonly = true)
+    {
+        $commandID = strtoupper($commandID);
+
+        if ($readonly) {
+            $this->readonly[$commandID] = $readonly;
+        }
+        else {
+            unset($this->readonly[$commandID]);
+        }
+    }
+
+    /**
+     * Marks a Lua script for EVAL and EVALSHA as a read-only operation. When
+     * the behaviour of a script can be decided only at runtime depending on
+     * its arguments, a callable object can be provided to dinamically check
+     * if the passed instance of EVAL or EVALSHA performs write operations or
+     * not.
+     *
+     * @param string $script Body of the Lua script.
+     * @param mixed $readonly A boolean or a callable object.
+     */
+    public function setScriptReadOnly($script, $readonly = true)
+    {
+        $sha1 = sha1($script);
+
+        if ($readonly) {
+            $this->readonlySHA1[$sha1] = $readonly;
+        }
+        else {
+            unset($this->readonlySHA1[$sha1]);
+        }
+    }
+
+    /**
+     * Returns the default list of commands performing read-only operations.
      *
      * @return array
      */