mirror of
https://github.com/predis/predis.git
synced 2026-08-19 18:01:59 +00:00
387 lines
14 KiB
PHP
387 lines
14 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This file is part of the Predis package.
|
|
*
|
|
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace Predis\Replication;
|
|
|
|
use PredisTestCase;
|
|
use Predis\Profile;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
class ReplicationStrategyTest extends PredisTestCase
|
|
{
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testReadCommands()
|
|
{
|
|
$profile = Profile\Factory::getDevelopment();
|
|
$strategy = new ReplicationStrategy();
|
|
|
|
foreach ($this->getExpectedCommands('read') as $commandId) {
|
|
$command = $profile->createCommand($commandId);
|
|
$this->assertTrue($strategy->isReadOperation($command));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testWriteRequests()
|
|
{
|
|
$profile = Profile\Factory::getDevelopment();
|
|
$strategy = new ReplicationStrategy();
|
|
|
|
foreach ($this->getExpectedCommands('write') as $commandId) {
|
|
$command = $profile->createCommand($commandId);
|
|
$this->assertFalse($strategy->isReadOperation($command), $commandId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testDisallowedCommands()
|
|
{
|
|
$profile = Profile\Factory::getDevelopment();
|
|
$strategy = new ReplicationStrategy();
|
|
|
|
foreach ($this->getExpectedCommands('disallowed') as $commandId) {
|
|
$command = $profile->createCommand($commandId);
|
|
$this->assertTrue($strategy->isDisallowedOperation($command), $commandId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testSortCommand()
|
|
{
|
|
$profile = Profile\Factory::getDevelopment();
|
|
$strategy = new ReplicationStrategy();
|
|
|
|
$cmdReadSort = $profile->createCommand('SORT', array('key:list'));
|
|
$this->assertTrue($strategy->isReadOperation($cmdReadSort), 'SORT [read-only]');
|
|
|
|
$cmdWriteSort = $profile->createCommand('SORT', array('key:list', array('store' => 'key:stored')));
|
|
$this->assertFalse($strategy->isReadOperation($cmdWriteSort), 'SORT [write with STORE]');
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
* @expectedException \Predis\NotSupportedException
|
|
* @expectedExceptionMessage The command 'INFO' is not allowed in replication mode.
|
|
*/
|
|
public function testUsingDisallowedCommandThrowsException()
|
|
{
|
|
$profile = Profile\Factory::getDevelopment();
|
|
$strategy = new ReplicationStrategy();
|
|
|
|
$command = $profile->createCommand('INFO');
|
|
$strategy->isReadOperation($command);
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testDefaultIsWriteOperation()
|
|
{
|
|
$strategy = new ReplicationStrategy();
|
|
|
|
$command = $this->getMock('Predis\Command\CommandInterface');
|
|
$command->expects($this->any())
|
|
->method('getId')
|
|
->will($this->returnValue('CMDTEST'));
|
|
|
|
$this->assertFalse($strategy->isReadOperation($command));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testCanSetCommandAsReadOperation()
|
|
{
|
|
$strategy = new ReplicationStrategy();
|
|
|
|
$command = $this->getMock('Predis\Command\CommandInterface');
|
|
$command->expects($this->any())
|
|
->method('getId')
|
|
->will($this->returnValue('CMDTEST'));
|
|
|
|
$strategy->setCommandReadOnly('CMDTEST', true);
|
|
$this->assertTrue($strategy->isReadOperation($command));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testCanSetCommandAsWriteOperation()
|
|
{
|
|
$strategy = new ReplicationStrategy();
|
|
|
|
$command = $this->getMock('Predis\Command\CommandInterface');
|
|
$command->expects($this->any())
|
|
->method('getId')
|
|
->will($this->returnValue('CMDTEST'));
|
|
|
|
$strategy->setCommandReadOnly('CMDTEST', false);
|
|
$this->assertFalse($strategy->isReadOperation($command));
|
|
|
|
$strategy->setCommandReadOnly('GET', false);
|
|
$this->assertFalse($strategy->isReadOperation($command));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testCanUseCallableToCheckCommand()
|
|
{
|
|
$strategy = new ReplicationStrategy();
|
|
$profile = Profile\Factory::getDevelopment();
|
|
|
|
$strategy->setCommandReadOnly('SET', function ($command) {
|
|
return $command->getArgument(1) === true;
|
|
});
|
|
|
|
$command = $profile->createCommand('SET', array('trigger', false));
|
|
$this->assertFalse($strategy->isReadOperation($command));
|
|
|
|
$command = $profile->createCommand('SET', array('trigger', true));
|
|
$this->assertTrue($strategy->isReadOperation($command));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testSetLuaScriptAsReadOperation()
|
|
{
|
|
$strategy = new ReplicationStrategy();
|
|
$profile = Profile\Factory::getDevelopment();
|
|
|
|
$writeScript = 'redis.call("set", "foo", "bar")';
|
|
$readScript = 'return true';
|
|
|
|
$strategy->setScriptReadOnly($readScript, true);
|
|
|
|
$cmdEval = $profile->createCommand('EVAL', array($writeScript));
|
|
$cmdEvalSHA = $profile->createCommand('EVALSHA', array(sha1($writeScript)));
|
|
$this->assertFalse($strategy->isReadOperation($cmdEval));
|
|
$this->assertFalse($strategy->isReadOperation($cmdEvalSHA));
|
|
|
|
$cmdEval = $profile->createCommand('EVAL', array($readScript));
|
|
$cmdEvalSHA = $profile->createCommand('EVALSHA', array(sha1($readScript)));
|
|
$this->assertTrue($strategy->isReadOperation($cmdEval));
|
|
$this->assertTrue($strategy->isReadOperation($cmdEvalSHA));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testSetLuaScriptAsReadOperationWorksWithScriptCommand()
|
|
{
|
|
$strategy = new ReplicationStrategy();
|
|
|
|
$command = $this->getMock('Predis\Command\ScriptCommand', array('getScript'));
|
|
$command->expects($this->any())
|
|
->method('getScript')
|
|
->will($this->returnValue($script = 'return true'));
|
|
|
|
$strategy->setScriptReadOnly($script, function ($command) {
|
|
return $command->getArgument(2) === true;
|
|
});
|
|
|
|
$command->setArguments(array(false));
|
|
$this->assertFalse($strategy->isReadOperation($command));
|
|
|
|
$command->setArguments(array(true));
|
|
$this->assertTrue($strategy->isReadOperation($command));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testSetLuaScriptAsReadOperationWorksWithScriptCommandAndCallableCheck()
|
|
{
|
|
$strategy = new ReplicationStrategy();
|
|
|
|
$command = $this->getMock('Predis\Command\ScriptCommand', array('getScript'));
|
|
$command->expects($this->any())
|
|
->method('getScript')
|
|
->will($this->returnValue($script = 'return true'));
|
|
|
|
$command->setArguments(array('trigger', false));
|
|
|
|
$strategy->setScriptReadOnly($script, true);
|
|
|
|
$this->assertTrue($strategy->isReadOperation($command));
|
|
}
|
|
|
|
// ******************************************************************** //
|
|
// ---- HELPER METHODS ------------------------------------------------ //
|
|
// ******************************************************************** //
|
|
|
|
/**
|
|
* Returns the list of expected supported commands.
|
|
*
|
|
* @param string $type Optional type of command (based on its keys)
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function getExpectedCommands($type = null)
|
|
{
|
|
$commands = array(
|
|
/* commands operating on the connection */
|
|
'AUTH' => 'read',
|
|
'SELECT' => 'read',
|
|
'ECHO' => 'read',
|
|
'QUIT' => 'read',
|
|
'OBJECT' => 'read',
|
|
'TIME' => 'read',
|
|
'SHUTDOWN' => 'disallowed',
|
|
'INFO' => 'disallowed',
|
|
'DBSIZE' => 'disallowed',
|
|
'LASTSAVE' => 'disallowed',
|
|
'CONFIG' => 'disallowed',
|
|
'MONITOR' => 'disallowed',
|
|
'SLAVEOF' => 'disallowed',
|
|
'SAVE' => 'disallowed',
|
|
'BGSAVE' => 'disallowed',
|
|
'BGREWRITEAOF' => 'disallowed',
|
|
'SLOWLOG' => 'disallowed',
|
|
|
|
/* commands operating on the key space */
|
|
'EXISTS' => 'read',
|
|
'DEL' => 'write',
|
|
'TYPE' => 'read',
|
|
'EXPIRE' => 'write',
|
|
'EXPIREAT' => 'write',
|
|
'PERSIST' => 'write',
|
|
'PEXPIRE' => 'write',
|
|
'PEXPIREAT' => 'write',
|
|
'TTL' => 'read',
|
|
'PTTL' => 'write',
|
|
'SORT' => 'variable',
|
|
'KEYS' => 'read',
|
|
'SCAN' => 'read',
|
|
'RANDOMKEY' => 'read',
|
|
|
|
/* commands operating on string values */
|
|
'APPEND' => 'write',
|
|
'DECR' => 'write',
|
|
'DECRBY' => 'write',
|
|
'GET' => 'read',
|
|
'GETBIT' => 'read',
|
|
'BITCOUNT' => 'read',
|
|
'BITPOS' => 'read',
|
|
'BITOP' => 'write',
|
|
'MGET' => 'read',
|
|
'SET' => 'write',
|
|
'GETRANGE' => 'read',
|
|
'GETSET' => 'write',
|
|
'INCR' => 'write',
|
|
'INCRBY' => 'write',
|
|
'INCRBYFLOAT' => 'write',
|
|
'SETBIT' => 'write',
|
|
'SETEX' => 'write',
|
|
'MSET' => 'write',
|
|
'MSETNX' => 'write',
|
|
'SETNX' => 'write',
|
|
'SETRANGE' => 'write',
|
|
'STRLEN' => 'read',
|
|
'SUBSTR' => 'read',
|
|
|
|
/* commands operating on lists */
|
|
'LINSERT' => 'write',
|
|
'LINDEX' => 'read',
|
|
'LLEN' => 'read',
|
|
'LPOP' => 'write',
|
|
'RPOP' => 'write',
|
|
'BLPOP' => 'write',
|
|
'BRPOP' => 'write',
|
|
'LPUSH' => 'write',
|
|
'LPUSHX' => 'write',
|
|
'RPUSH' => 'write',
|
|
'RPUSHX' => 'write',
|
|
'LRANGE' => 'read',
|
|
'LREM' => 'write',
|
|
'LSET' => 'write',
|
|
'LTRIM' => 'write',
|
|
|
|
/* commands operating on sets */
|
|
'SADD' => 'write',
|
|
'SCARD' => 'read',
|
|
'SISMEMBER' => 'read',
|
|
'SMEMBERS' => 'read',
|
|
'SSCAN' => 'read',
|
|
'SRANDMEMBER' => 'read',
|
|
'SPOP' => 'write',
|
|
'SREM' => 'write',
|
|
'SINTER' => 'read',
|
|
'SUNION' => 'read',
|
|
'SDIFF' => 'read',
|
|
|
|
/* commands operating on sorted sets */
|
|
'ZADD' => 'write',
|
|
'ZCARD' => 'read',
|
|
'ZCOUNT' => 'read',
|
|
'ZINCRBY' => 'write',
|
|
'ZRANGE' => 'read',
|
|
'ZRANGEBYSCORE' => 'read',
|
|
'ZRANK' => 'read',
|
|
'ZREM' => 'write',
|
|
'ZREMRANGEBYRANK' => 'write',
|
|
'ZREMRANGEBYSCORE' => 'write',
|
|
'ZREVRANGE' => 'read',
|
|
'ZREVRANGEBYSCORE' => 'read',
|
|
'ZREVRANK' => 'read',
|
|
'ZSCORE' => 'read',
|
|
'ZSCAN' => 'read',
|
|
'ZLEXCOUNT' => 'read',
|
|
'ZRANGEBYLEX' => 'read',
|
|
'ZREMRANGEBYLEX' => 'write',
|
|
'ZREVRANGEBYLEX' => 'read',
|
|
|
|
/* commands operating on hashes */
|
|
'HDEL' => 'write',
|
|
'HEXISTS' => 'read',
|
|
'HGET' => 'read',
|
|
'HGETALL' => 'read',
|
|
'HMGET' => 'read',
|
|
'HINCRBY' => 'write',
|
|
'HINCRBYFLOAT' => 'write',
|
|
'HKEYS' => 'read',
|
|
'HLEN' => 'read',
|
|
'HSET' => 'write',
|
|
'HSETNX' => 'write',
|
|
'HVALS' => 'read',
|
|
'HSCAN' => 'read',
|
|
|
|
/* commands operating on HyperLogLog */
|
|
'PFADD' => 'write',
|
|
'PFMERGE' => 'write',
|
|
'PFCOUNT' => 'read',
|
|
|
|
/* scripting */
|
|
'EVAL' => 'write',
|
|
'EVALSHA' => 'write',
|
|
);
|
|
|
|
if (isset($type)) {
|
|
$commands = array_filter($commands, function ($expectedType) use ($type) {
|
|
return $expectedType === $type;
|
|
});
|
|
}
|
|
|
|
return array_keys($commands);
|
|
}
|
|
}
|