浏览代码

Support EVAL and EVALSHA with client-side sharding (Predis cluster).

We extract the keys from commands using the second argument of EVAL /
EVALSHA which specifies the number of arguments that must be treated
as keys (used to populate the KEYS table in the Lua script) and then
we check if all the keys generate the same hash using the usual method.

Our scripted command abstraction is also supported.

It comes without saying that accessing or setting keys from within the
Lua script is something that might not work as expected, so you should
be careful when using EVAL and EVALSHA in the context of client-side
sharding.
Daniele Alessandri 13 年之前
父节点
当前提交
b2f59a0c63

+ 22 - 0
lib/Predis/Cluster/PredisClusterHashStrategy.php

@@ -13,6 +13,7 @@ namespace Predis\Cluster;
 
 
 use Predis\Cluster\Hash\HashGeneratorInterface;
 use Predis\Cluster\Hash\HashGeneratorInterface;
 use Predis\Command\CommandInterface;
 use Predis\Command\CommandInterface;
+use Predis\Command\ScriptedCommand;
 
 
 /**
 /**
  * Default class used by Predis for client-side sharding to calculate
  * Default class used by Predis for client-side sharding to calculate
@@ -146,6 +147,10 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
             'HSET'                  => $keyIsFirstArgument,
             'HSET'                  => $keyIsFirstArgument,
             'HSETNX'                => $keyIsFirstArgument,
             'HSETNX'                => $keyIsFirstArgument,
             'HVALS'                 => $keyIsFirstArgument,
             'HVALS'                 => $keyIsFirstArgument,
+
+            /* scripting */
+            'EVAL'                  => array($this, 'getKeyFromScriptingCommands'),
+            'EVALSHA'               => array($this, 'getKeyFromScriptingCommands'),
         );
         );
     }
     }
 
 
@@ -281,6 +286,23 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
         }
         }
     }
     }
 
 
+    /**
+     * Extracts the key from EVAL and EVALSHA commands.
+     *
+     * @param CommandInterface $command Command instance.
+     * @return string
+     */
+    protected function getKeyFromScriptingCommands(CommandInterface $command)
+    {
+        $keys = $command instanceof ScriptedCommand
+                    ? $command->getKeys()
+                    : array_slice($args = $command->getArguments(), 2, $args[1]);
+
+        if ($keys && $this->checkSameHashForKeys($keys)) {
+            return $keys[0];
+        }
+    }
+
     /**
     /**
      * {@inheritdoc}
      * {@inheritdoc}
      */
      */

+ 39 - 0
tests/Predis/Cluster/PredisClusterHashStrategyTest.php

@@ -149,6 +149,41 @@ class PredisClusterHashStrategyTest extends StandardTestCase
         }
         }
     }
     }
 
 
+    /**
+     * @group disconnected
+     */
+    public function testKeysForScriptCommand()
+    {
+        $strategy = $this->getHashStrategy();
+        $profile = ServerProfile::getDevelopment();
+        $arguments = array('%SCRIPT%', 2, '{key}:1', '{key}:2', 'value1', 'value2');
+
+        foreach ($this->getExpectedCommands('keys-script') as $commandID) {
+            $command = $profile->createCommand($commandID, $arguments);
+            $this->assertNotNull($strategy->getHash($command), $commandID);
+        }
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testKeysForScriptedCommand()
+    {
+        $strategy = $this->getHashStrategy();
+        $arguments = array('{key}:1', '{key}:2', 'value1', 'value2');
+
+        $command = $this->getMock('Predis\Command\ScriptedCommand', array('getScript', 'getKeysCount'));
+        $command->expects($this->once())
+                ->method('getScript')
+                ->will($this->returnValue('return true'));
+        $command->expects($this->exactly(2))
+                ->method('getKeysCount')
+                ->will($this->returnValue(2));
+        $command->setArguments($arguments);
+
+        $this->assertNotNull($strategy->getHash($command), "Scripted Command [{$command->getId()}]");
+    }
+
     /**
     /**
      * @group disconnected
      * @group disconnected
      */
      */
@@ -315,6 +350,10 @@ class PredisClusterHashStrategyTest extends StandardTestCase
             'HSET'                  => 'keys-first',
             'HSET'                  => 'keys-first',
             'HSETNX'                => 'keys-first',
             'HSETNX'                => 'keys-first',
             'HVALS'                 => 'keys-first',
             'HVALS'                 => 'keys-first',
+
+            /* scripting */
+            'EVAL'                  => 'keys-script',
+            'EVALSHA'               => 'keys-script',
         );
         );
 
 
         if (isset($type)) {
         if (isset($type)) {