Permalink
Browse files

Extract a strategy class from the replication connection.

  • Loading branch information...
1 parent b4e1ab1 commit 732d7f96d1e57c2d5c268f5a0e5323310edb5084 @nrk committed Sep 12, 2012
View
@@ -45,6 +45,10 @@ v0.8.0 (201x-xx-xx)
- Cluster and replication connections now extend a new common interface,
`Predis\Connection\AggregatedConnectionInterface`.
+- The `Predis\Connection\MasterSlaveReplication` connection class now uses an
+ external strategy class to handle the logic for checking readable / writable
+ commands or Lua scripts.
+
- Command pipelines have been optimized for both speed and code cleanness, but
at the cost of bringing a breaking change in the signature of the interface
for pipeline executors.
@@ -21,6 +21,7 @@
use Predis\Command\ScriptedCommand;
use Predis\Connection\MasterSlaveReplication;
use Predis\Profile\ServerProfile;
+use Predis\Replication\ReplicationStrategy;
// ------------------------------------------------------------------------- //
@@ -57,8 +58,10 @@ public function getScript() {
return $profile;
},
'replication' => function ($options) {
- $replication = new MasterSlaveReplication();
- $replication->setScriptReadOnly(HashMultipleGetAll::BODY);
+ $strategy = new ReplicationStrategy();
+ $strategy->setScriptReadOnly(HashMultipleGetAll::BODY);
+
+ $replication = new MasterSlaveReplication($strategy);
return $replication;
},
@@ -13,30 +13,28 @@
use Predis\NotSupportedException;
use Predis\Command\CommandInterface;
+use Predis\Replication\ReplicationStrategy;
/**
- * Defines the standard virtual connection class that is used
- * by Predis to handle replication with a group of servers in
- * a master/slave configuration.
+ * Aggregated connection class used by to handle replication with a
+ * group of servers in a master/slave configuration.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class MasterSlaveReplication implements ReplicationConnectionInterface
{
- private $disallowed = array();
- private $readonly = array();
- private $readonlySHA1 = array();
- private $current = null;
- private $master = null;
- private $slaves = array();
+ protected $strategy;
+ protected $master;
+ protected $slaves;
+ protected $current;
/**
*
*/
- public function __construct()
+ public function __construct(ReplicationStrategy $strategy = null)
{
- $this->disallowed = $this->getDisallowedOperations();
- $this->readonly = $this->getReadOnlyOperations();
+ $this->slaves = array();
+ $this->strategy = $strategy ?: new ReplicationStrategy();
}
/**
@@ -102,15 +100,16 @@ public function getConnection(CommandInterface $command)
{
if ($this->current === null) {
$this->check();
- $this->current = $this->isReadOperation($command) ? $this->pickSlave() : $this->master;
+ $this->current = $this->strategy->isReadOperation($command) ? $this->pickSlave() : $this->master;
return $this->current;
}
+
if ($this->current === $this->master) {
return $this->current;
}
- if (!$this->isReadOperation($command)) {
+ if (!$this->strategy->isReadOperation($command)) {
$this->current = $this->master;
}
@@ -175,6 +174,16 @@ public function getSlaves()
}
/**
+ * Returns the underlying replication strategy.
+ *
+ * @return ReplicationStrategy
+ */
+ public function getReplicationStrategy()
+ {
+ return $this->strategy;
+ }
+
+ /**
* Returns a random slave.
*
* @return SingleConnectionInterface
@@ -244,179 +253,10 @@ public function executeCommand(CommandInterface $command)
}
/**
- * Returns if the specified command performs a read-only operation
- * against a key stored on Redis.
- *
- * @param CommandInterface $command Instance of Redis command.
- * @return Boolean
- */
- protected function isReadOperation(CommandInterface $command)
- {
- if (isset($this->disallowed[$id = $command->getId()])) {
- throw new NotSupportedException("The command $id is not allowed in replication mode");
- }
-
- if (isset($this->readonly[$id])) {
- if (true === $readonly = $this->readonly[$id]) {
- return true;
- }
-
- return call_user_func($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 call_user_func($readonly, $command);
- }
- }
-
- return false;
- }
-
- /**
- * Checks if a SORT command is a readable operation by parsing the arguments
- * array of the specified commad instance.
- *
- * @param CommandInterface $command Instance of Redis command.
- * @return Boolean
- */
- private function isSortReadOnly(CommandInterface $command)
- {
- $arguments = $command->getArguments();
- return ($c = count($arguments)) === 1 ? true : $arguments[$c - 2] !== 'STORE';
- }
-
- /**
- * 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 disallowed commands.
- *
- * @return array
- */
- protected function getDisallowedOperations()
- {
- return array(
- 'SHUTDOWN' => true,
- 'INFO' => true,
- 'DBSIZE' => true,
- 'LASTSAVE' => true,
- 'CONFIG' => true,
- 'MONITOR' => true,
- 'SLAVEOF' => true,
- 'SAVE' => true,
- 'BGSAVE' => true,
- 'BGREWRITEAOF' => true,
- 'SLOWLOG' => true,
- );
- }
-
- /**
- * Returns the default list of commands performing read-only operations.
- *
- * @return array
- */
- protected function getReadOnlyOperations()
- {
- return array(
- 'EXISTS' => true,
- 'TYPE' => true,
- 'KEYS' => true,
- 'RANDOMKEY' => true,
- 'TTL' => true,
- 'GET' => true,
- 'MGET' => true,
- 'SUBSTR' => true,
- 'STRLEN' => true,
- 'GETRANGE' => true,
- 'GETBIT' => true,
- 'LLEN' => true,
- 'LRANGE' => true,
- 'LINDEX' => true,
- 'SCARD' => true,
- 'SISMEMBER' => true,
- 'SINTER' => true,
- 'SUNION' => true,
- 'SDIFF' => true,
- 'SMEMBERS' => true,
- 'SRANDMEMBER' => true,
- 'ZRANGE' => true,
- 'ZREVRANGE' => true,
- 'ZRANGEBYSCORE' => true,
- 'ZREVRANGEBYSCORE' => true,
- 'ZCARD' => true,
- 'ZSCORE' => true,
- 'ZCOUNT' => true,
- 'ZRANK' => true,
- 'ZREVRANK' => true,
- 'HGET' => true,
- 'HMGET' => true,
- 'HEXISTS' => true,
- 'HLEN' => true,
- 'HKEYS' => true,
- 'HVELS' => true,
- 'HGETALL' => true,
- 'PING' => true,
- 'AUTH' => true,
- 'SELECT' => true,
- 'ECHO' => true,
- 'QUIT' => true,
- 'OBJECT' => true,
- 'BITCOUNT' => true,
- 'TIME' => true,
- 'SORT' => array($this, 'isSortReadOnly'),
- );
- }
-
- /**
* {@inheritdoc}
*/
public function __sleep()
{
- return array('master', 'slaves', 'disallowed', 'readonly', 'readonlySHA1');
+ return array('master', 'slaves', 'strategy');
}
}
Oops, something went wrong.

0 comments on commit 732d7f9

Please sign in to comment.