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.
This commit is contained in:
Daniele Alessandri
2011-12-17 10:58:37 +01:00
parent 6f4347010d
commit 5148ce16c6
+77 -21
View File
@@ -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
*/