mirror of
https://github.com/predis/predis.git
synced 2026-10-14 07:47:49 +00:00
62b421f20e
This change reduces some unnecessary complexity in the library, Redis commands do not change much after all. Developers can still implement their own commands factory, inject new commands or override existing ones. The "profile" client options has been renamed to "commands" and it accepts instances of Predis\Command\FactoryInterface. The test suite checks at runtime the version of the running instance of Redis for integration tests to adapt itself automatically.
1160 lines
43 KiB
PHP
1160 lines
43 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\Connection\Aggregate;
|
|
|
|
use Predis\Command;
|
|
use Predis\Connection;
|
|
use Predis\Response;
|
|
use PredisTestCase;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
class RedisClusterTest extends PredisTestCase
|
|
{
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testAcceptsCustomConnectionFactory()
|
|
{
|
|
$factory = $this->getMock('Predis\Connection\FactoryInterface');
|
|
$cluster = new RedisCluster($factory);
|
|
|
|
$this->assertSame($factory, $cluster->getConnectionFactory());
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testUsesRedisClusterStrategyByDefault()
|
|
{
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
|
|
$this->assertInstanceOf('Predis\Cluster\RedisStrategy', $cluster->getClusterStrategy());
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testAcceptsCustomClusterStrategy()
|
|
{
|
|
$strategy = $this->getMock('Predis\Cluster\StrategyInterface');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory(), $strategy);
|
|
|
|
$this->assertSame($strategy, $cluster->getClusterStrategy());
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testAddingConnectionsToCluster()
|
|
{
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
|
|
$this->assertSame(2, count($cluster));
|
|
$this->assertSame($connection1, $cluster->getConnectionById('127.0.0.1:6379'));
|
|
$this->assertSame($connection2, $cluster->getConnectionById('127.0.0.1:6380'));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testRemovingConnectionsFromCluster()
|
|
{
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6371');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
|
|
$this->assertTrue($cluster->remove($connection1));
|
|
$this->assertFalse($cluster->remove($connection3));
|
|
$this->assertSame(1, count($cluster));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testRemovingConnectionsFromClusterByAlias()
|
|
{
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
|
|
$this->assertTrue($cluster->removeById('127.0.0.1:6380'));
|
|
$this->assertFalse($cluster->removeById('127.0.0.1:6390'));
|
|
$this->assertSame(1, count($cluster));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testCountReturnsNumberOfConnectionsInPool()
|
|
{
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6381');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
$cluster->add($connection3);
|
|
|
|
$this->assertSame(3, count($cluster));
|
|
|
|
$cluster->remove($connection3);
|
|
|
|
$this->assertSame(2, count($cluster));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testConnectPicksRandomConnection()
|
|
{
|
|
$connect1 = false;
|
|
$connect2 = false;
|
|
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection1->expects($this->any())
|
|
->method('connect')
|
|
->will($this->returnCallback(function () use (&$connect1) {
|
|
$connect1 = true;
|
|
}));
|
|
$connection1->expects($this->any())
|
|
->method('isConnected')
|
|
->will($this->returnCallback(function () use (&$connect1) {
|
|
return $connect1;
|
|
}));
|
|
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
$connection2->expects($this->any())
|
|
->method('connect')
|
|
->will($this->returnCallback(function () use (&$connect2) {
|
|
$connect2 = true;
|
|
}));
|
|
$connection2->expects($this->any())
|
|
->method('isConnected')
|
|
->will($this->returnCallback(function () use (&$connect2) {
|
|
return $connect2;
|
|
}));
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
|
|
$cluster->connect();
|
|
|
|
$this->assertTrue($cluster->isConnected());
|
|
|
|
if ($connect1) {
|
|
$this->assertTrue($connect1);
|
|
$this->assertFalse($connect2);
|
|
} else {
|
|
$this->assertFalse($connect1);
|
|
$this->assertTrue($connect2);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testDisconnectForcesAllConnectionsToDisconnect()
|
|
{
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection1->expects($this->once())->method('disconnect');
|
|
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
$connection2->expects($this->once())->method('disconnect');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
|
|
$cluster->disconnect();
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testIsConnectedReturnsTrueIfAtLeastOneConnectionIsOpen()
|
|
{
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection1->expects($this->once())
|
|
->method('isConnected')
|
|
->will($this->returnValue(false));
|
|
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
$connection2->expects($this->once())
|
|
->method('isConnected')
|
|
->will($this->returnValue(true));
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
|
|
$this->assertTrue($cluster->isConnected());
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testIsConnectedReturnsFalseIfAllConnectionsAreClosed()
|
|
{
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection1->expects($this->once())
|
|
->method('isConnected')
|
|
->will($this->returnValue(false));
|
|
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
$connection2->expects($this->once())
|
|
->method('isConnected')
|
|
->will($this->returnValue(false));
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
|
|
$this->assertFalse($cluster->isConnected());
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testGetIteratorReturnsConnectionsMappedInSlotsMapWhenUseClusterSlotsIsDisabled()
|
|
{
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5460');
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10921');
|
|
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10922-16383');
|
|
$connection4 = $this->getMockConnection('tcp://127.0.0.1:6384');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
|
|
$cluster->useClusterSlots(false);
|
|
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
$cluster->add($connection3);
|
|
$cluster->add($connection4);
|
|
|
|
$this->assertInstanceOf('Iterator', $iterator = $cluster->getIterator());
|
|
$connections = iterator_to_array($iterator);
|
|
|
|
$this->assertCount(3, $connections);
|
|
$this->assertSame($connection1, $connections[0]);
|
|
$this->assertSame($connection2, $connections[1]);
|
|
$this->assertSame($connection3, $connections[2]);
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testGetIteratorReturnsConnectionsMappedInSlotsMapFetchedFromRedisCluster()
|
|
{
|
|
$slotsmap = array(
|
|
array(0, 5460, array('127.0.0.1', 6381), array()),
|
|
array(5461, 10921, array('127.0.0.1', 6383), array()),
|
|
array(10922, 16383, array('127.0.0.1', 6384), array()),
|
|
);
|
|
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5460');
|
|
$connection1->expects($this->once())
|
|
->method('executeCommand')
|
|
->with($this->isRedisCommand(
|
|
'CLUSTER', array('SLOTS')
|
|
))
|
|
->will($this->returnValue($slotsmap));
|
|
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10921');
|
|
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6383');
|
|
$connection4 = $this->getMockConnection('tcp://127.0.0.1:6384');
|
|
|
|
$factory = $this->getMock('Predis\Connection\FactoryInterface');
|
|
$factory->expects($this->at(0))
|
|
->method('create')
|
|
->with(array('host' => '127.0.0.1', 'port' => '6383'))
|
|
->will($this->returnValue($connection3));
|
|
$factory->expects($this->at(1))
|
|
->method('create')
|
|
->with(array('host' => '127.0.0.1', 'port' => '6384'))
|
|
->will($this->returnValue($connection4));
|
|
|
|
// TODO: I'm not sure about mocking a protected method, but it'll do for now
|
|
$cluster = $this->getMock('Predis\Connection\Aggregate\RedisCluster', array('getRandomConnection'), array($factory));
|
|
$cluster->expects($this->exactly(1))
|
|
->method('getRandomConnection')
|
|
->will($this->returnValue($connection1));
|
|
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
|
|
$cluster->useClusterSlots(true);
|
|
|
|
$this->assertInstanceOf('Iterator', $iterator = $cluster->getIterator());
|
|
$connections = iterator_to_array($iterator);
|
|
|
|
$this->assertCount(3, $connections);
|
|
$this->assertSame($connection1, $connections[0]);
|
|
$this->assertSame($connection3, $connections[1]);
|
|
$this->assertSame($connection4, $connections[2]);
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testCanAssignConnectionsToCustomSlots()
|
|
{
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6381');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
$cluster->add($connection3);
|
|
|
|
$cluster->setSlots(0, 1364, '127.0.0.1:6379');
|
|
$cluster->setSlots(1365, 2729, '127.0.0.1:6380');
|
|
$cluster->setSlots(2730, 4095, '127.0.0.1:6381');
|
|
|
|
$expectedMap = array_merge(
|
|
array_fill(0, 1365, '127.0.0.1:6379'),
|
|
array_fill(1364, 1365, '127.0.0.1:6380'),
|
|
array_fill(2729, 1366, '127.0.0.1:6381')
|
|
);
|
|
|
|
$this->assertSame($expectedMap, $cluster->getSlotsMap());
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testAddingConnectionResetsSlotsMap()
|
|
{
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($connection1);
|
|
|
|
$cluster->setSlots(0, 4095, '127.0.0.1:6379');
|
|
$this->assertSame(array_fill(0, 4096, '127.0.0.1:6379'), $cluster->getSlotsMap());
|
|
|
|
$cluster->add($connection2);
|
|
|
|
$this->assertEmpty($cluster->getSlotsMap());
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testRemovingConnectionResetsSlotsMap()
|
|
{
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
|
|
$cluster->setSlots(0, 2047, '127.0.0.1:6379');
|
|
$cluster->setSlots(2048, 4095, '127.0.0.1:6380');
|
|
|
|
$expectedMap = array_merge(
|
|
array_fill(0, 2048, '127.0.0.1:6379'),
|
|
array_fill(2048, 2048, '127.0.0.1:6380')
|
|
);
|
|
|
|
$this->assertSame($expectedMap, $cluster->getSlotsMap());
|
|
|
|
$cluster->remove($connection1);
|
|
$this->assertEmpty($cluster->getSlotsMap());
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testCanAssignConnectionsToRangeOfSlotsFromParameters()
|
|
{
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379?slots=0-5460');
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380?slots=5461-10921');
|
|
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=10922-16383');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
$cluster->add($connection3);
|
|
|
|
$cluster->buildSlotsMap();
|
|
|
|
$expectedMap = array_merge(
|
|
array_fill(0, 5461, '127.0.0.1:6379'),
|
|
array_fill(5460, 5461, '127.0.0.1:6380'),
|
|
array_fill(10921, 5462, '127.0.0.1:6381')
|
|
);
|
|
|
|
$actualMap = $cluster->getSlotsMap();
|
|
ksort($actualMap);
|
|
|
|
$this->assertSame($expectedMap, $actualMap);
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testCanAssignConnectionsToSingleSlotOrRangesOfSlotsFromParameters()
|
|
{
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379?slots=0-5460,5500-5600,11000');
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380?slots=5461-5499,5600-10921');
|
|
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=10922-10999,11001-16383');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
$cluster->add($connection3);
|
|
|
|
$cluster->buildSlotsMap();
|
|
|
|
$expectedMap = array_merge(
|
|
array_fill(0, 5461, '127.0.0.1:6379'),
|
|
array_fill(5460, 39, '127.0.0.1:6380'),
|
|
array_fill(5499, 101, '127.0.0.1:6379'),
|
|
array_fill(5599, 5321, '127.0.0.1:6380'),
|
|
array_fill(10921, 78, '127.0.0.1:6381'),
|
|
array_fill(11000, 1, '127.0.0.1:6379'),
|
|
array_fill(11000, 5383, '127.0.0.1:6381')
|
|
);
|
|
|
|
$actualMap = $cluster->getSlotsMap();
|
|
ksort($actualMap);
|
|
|
|
$this->assertSame($expectedMap, $actualMap);
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testReturnsCorrectConnectionUsingSlotID()
|
|
{
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6381');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
$cluster->add($connection3);
|
|
|
|
$this->assertSame($connection1, $cluster->getConnectionBySlot(0));
|
|
$this->assertSame($connection2, $cluster->getConnectionBySlot(5461));
|
|
$this->assertSame($connection3, $cluster->getConnectionBySlot(10922));
|
|
|
|
$cluster->setSlots(5461, 7096, '127.0.0.1:6380');
|
|
$this->assertSame($connection2, $cluster->getConnectionBySlot(5461));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testReturnsCorrectConnectionUsingCommandInstance()
|
|
{
|
|
$commands = $this->getCommandFactory();
|
|
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6381');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
$cluster->add($connection3);
|
|
|
|
$set = $commands->createCommand('set', array('node:1001', 'foobar'));
|
|
$get = $commands->createCommand('get', array('node:1001'));
|
|
$this->assertSame($connection1, $cluster->getConnection($set));
|
|
$this->assertSame($connection1, $cluster->getConnection($get));
|
|
|
|
$set = $commands->createCommand('set', array('node:1048', 'foobar'));
|
|
$get = $commands->createCommand('get', array('node:1048'));
|
|
$this->assertSame($connection2, $cluster->getConnection($set));
|
|
$this->assertSame($connection2, $cluster->getConnection($get));
|
|
|
|
$set = $commands->createCommand('set', array('node:1082', 'foobar'));
|
|
$get = $commands->createCommand('get', array('node:1082'));
|
|
$this->assertSame($connection3, $cluster->getConnection($set));
|
|
$this->assertSame($connection3, $cluster->getConnection($get));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testWritesCommandToCorrectConnection()
|
|
{
|
|
$command = $this->getCommandFactory()->createCommand('get', array('node:1001'));
|
|
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection1->expects($this->once())->method('writeRequest')->with($command);
|
|
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
$connection2->expects($this->never())->method('writeRequest');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->useClusterSlots(false);
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
|
|
$cluster->writeRequest($command);
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testReadsCommandFromCorrectConnection()
|
|
{
|
|
$command = $this->getCommandFactory()->createCommand('get', array('node:1050'));
|
|
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection1->expects($this->never())->method('readResponse');
|
|
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
$connection2->expects($this->once())->method('readResponse')->with($command);
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->useClusterSlots(false);
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
|
|
$cluster->readResponse($command);
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testRetriesExecutingCommandOnConnectionFailureOnlyAfterFetchingNewSlotsMap()
|
|
{
|
|
$slotsmap = array(
|
|
array(0, 5460, array('127.0.0.1', 9381), array()),
|
|
array(5461, 10921, array('127.0.0.1', 6382), array()),
|
|
array(10922, 16383, array('127.0.0.1', 6383), array()),
|
|
);
|
|
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5460');
|
|
$connection1->expects($this->once())
|
|
->method('executeCommand')
|
|
->with($this->isRedisCommand(
|
|
'GET', array('node:1001')
|
|
))
|
|
->will($this->throwException(
|
|
new Connection\ConnectionException($connection1, 'Unknown connection error [127.0.0.1:6381]')
|
|
));
|
|
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10921');
|
|
$connection2->expects($this->any())
|
|
->method('executeCommand')
|
|
->with($this->isRedisCommand(
|
|
'CLUSTER', array('SLOTS')
|
|
))
|
|
->will($this->returnValue($slotsmap));
|
|
|
|
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10922-16383');
|
|
$connection3->expects($this->any())
|
|
->method('executeCommand')
|
|
->with($this->isRedisCommand(
|
|
'CLUSTER', array('SLOTS')
|
|
))
|
|
->will($this->returnValue($slotsmap));
|
|
|
|
$connection4 = $this->getMockConnection('tcp://127.0.0.1:9381');
|
|
$connection4->expects($this->at(0))
|
|
->method('executeCommand')
|
|
->with($this->isRedisCommand(
|
|
'GET', array('node:1001')
|
|
))
|
|
->will($this->returnValue('value:1001'));
|
|
$connection4->expects($this->at(1))
|
|
->method('executeCommand')
|
|
->with($this->isRedisCommand(
|
|
'GET', array('node:5001')
|
|
))
|
|
->will($this->returnValue('value:5001'));
|
|
|
|
$factory = $this->getMock('Predis\Connection\FactoryInterface');
|
|
$factory->expects($this->once())
|
|
->method('create')
|
|
->with(array(
|
|
'host' => '127.0.0.1',
|
|
'port' => '9381',
|
|
))
|
|
->will($this->returnValue($connection4));
|
|
|
|
$cluster = new RedisCluster($factory);
|
|
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
$cluster->add($connection3);
|
|
|
|
$this->assertSame('value:1001', $cluster->executeCommand(
|
|
Command\RawCommand::create('get', 'node:1001')
|
|
));
|
|
|
|
$this->assertSame('value:5001', $cluster->executeCommand(
|
|
Command\RawCommand::create('get', 'node:5001')
|
|
));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testRetriesExecutingCommandOnConnectionFailureButDoNotAskSlotsMapWhenDisabled()
|
|
{
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5500');
|
|
$connection1->expects($this->once())
|
|
->method('executeCommand')
|
|
->with($this->isRedisCommand(
|
|
'GET', array('node:1001')
|
|
))
|
|
->will($this->throwException(
|
|
new Connection\ConnectionException($connection1, 'Unknown connection error [127.0.0.1:6381]')
|
|
));
|
|
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5501-11000');
|
|
$connection2->expects($this->once())
|
|
->method('executeCommand')
|
|
->with($this->isRedisCommand(
|
|
'GET', array('node:1001')
|
|
))
|
|
->will($this->returnValue(
|
|
new Response\Error('MOVED 1970 127.0.0.1:9381')
|
|
));
|
|
|
|
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=11101-16383');
|
|
$connection3->expects($this->never())
|
|
->method('executeCommand');
|
|
|
|
$connection4 = $this->getMockConnection('tcp://127.0.0.1:9381');
|
|
$connection4->expects($this->once())
|
|
->method('executeCommand')
|
|
->with($this->isRedisCommand(
|
|
'GET', array('node:1001')
|
|
))
|
|
->will($this->returnValue('value:1001'));
|
|
|
|
$factory = $this->getMock('Predis\Connection\FactoryInterface');
|
|
$factory->expects($this->once())
|
|
->method('create')
|
|
->with(array(
|
|
'host' => '127.0.0.1',
|
|
'port' => '9381',
|
|
))
|
|
->will($this->returnValue($connection4));
|
|
|
|
// TODO: I'm not sure about mocking a protected method, but it'll do for now
|
|
$cluster = $this->getMock('Predis\Connection\Aggregate\RedisCluster', array('getRandomConnection'), array($factory));
|
|
$cluster->expects($this->never())
|
|
->method('getRandomConnection');
|
|
|
|
$cluster->useClusterSlots(false);
|
|
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
$cluster->add($connection3);
|
|
|
|
$this->assertSame('value:1001', $cluster->executeCommand(
|
|
Command\RawCommand::create('get', 'node:1001')
|
|
));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
* @expectedException \Predis\ClientException
|
|
* @expectedExceptionMessage No connections available in the pool
|
|
*/
|
|
public function testThrowsClientExceptionWhenExecutingCommandWithEmptyPool()
|
|
{
|
|
$factory = $this->getMock('Predis\Connection\FactoryInterface');
|
|
$factory->expects($this->never())->method('create');
|
|
|
|
$cluster = new RedisCluster($factory);
|
|
|
|
$cluster->executeCommand(Command\RawCommand::create('get', 'node:1001'));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testAskSlotsMapReturnEmptyArrayOnEmptyConnectionsPool()
|
|
{
|
|
$factory = $this->getMock('Predis\Connection\FactoryInterface');
|
|
$factory->expects($this->never())->method('create');
|
|
|
|
$cluster = new RedisCluster($factory);
|
|
|
|
$this->assertEmpty($cluster->askSlotsMap());
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testAskSlotsMapRetriesOnDifferentNodeOnConnectionFailure()
|
|
{
|
|
$slotsmap = array(
|
|
array(0, 5460, array('127.0.0.1', 9381), array()),
|
|
array(5461, 10921, array('127.0.0.1', 6382), array()),
|
|
array(10922, 16383, array('127.0.0.1', 6383), array()),
|
|
);
|
|
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5460');
|
|
$connection1->expects($this->once())
|
|
->method('executeCommand')
|
|
->with($this->isRedisCommand(
|
|
'CLUSTER', array('SLOTS')
|
|
))
|
|
->will($this->throwException(
|
|
new Connection\ConnectionException($connection1, 'Unknown connection error [127.0.0.1:6381]')
|
|
));
|
|
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10921');
|
|
$connection2->expects($this->once())
|
|
->method('executeCommand')
|
|
->with($this->isRedisCommand(
|
|
'CLUSTER', array('SLOTS')
|
|
))
|
|
->will($this->throwException(
|
|
new Connection\ConnectionException($connection2, 'Unknown connection error [127.0.0.1:6383]')
|
|
));
|
|
|
|
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10922-16383');
|
|
$connection3->expects($this->once())
|
|
->method('executeCommand')
|
|
->with($this->isRedisCommand(
|
|
'CLUSTER', array('SLOTS')
|
|
))
|
|
->will($this->returnValue($slotsmap));
|
|
|
|
$factory = $this->getMock('Predis\Connection\FactoryInterface');
|
|
$factory->expects($this->never())->method('create');
|
|
|
|
// TODO: I'm not sure about mocking a protected method, but it'll do for now
|
|
$cluster = $this->getMock('Predis\Connection\Aggregate\RedisCluster', array('getRandomConnection'), array($factory));
|
|
$cluster->expects($this->exactly(3))
|
|
->method('getRandomConnection')
|
|
->will($this->onConsecutiveCalls($connection1, $connection2, $connection3));
|
|
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
$cluster->add($connection3);
|
|
|
|
$this->assertCount(16384, $cluster->askSlotsMap());
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
* @expectedException \Predis\Connection\ConnectionException
|
|
* @expectedExceptionMessage Unknown connection error [127.0.0.1:6382]
|
|
*/
|
|
public function testAskSlotsMapHonorsRetryLimitOnMultipleConnectionFailures()
|
|
{
|
|
$slotsmap = array(
|
|
array(0, 5460, array('127.0.0.1', 9381), array()),
|
|
array(5461, 10921, array('127.0.0.1', 6382), array()),
|
|
array(10922, 16383, array('127.0.0.1', 6383), array()),
|
|
);
|
|
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5460');
|
|
$connection1->expects($this->any())
|
|
->method('executeCommand')
|
|
->with($this->isRedisCommand(
|
|
'CLUSTER', array('SLOTS')
|
|
))
|
|
->will($this->throwException(
|
|
new Connection\ConnectionException($connection1, 'Unknown connection error [127.0.0.1:6381]')
|
|
));
|
|
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10921');
|
|
$connection2->expects($this->any())
|
|
->method('executeCommand')
|
|
->with($this->isRedisCommand(
|
|
'CLUSTER', array('SLOTS')
|
|
))
|
|
->will($this->throwException(
|
|
new Connection\ConnectionException($connection2, 'Unknown connection error [127.0.0.1:6382]')
|
|
));
|
|
|
|
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10922-16383');
|
|
$connection3->expects($this->never())
|
|
->method('executeCommand');
|
|
|
|
$factory = $this->getMock('Predis\Connection\FactoryInterface');
|
|
$factory->expects($this->never())->method('create');
|
|
|
|
// TODO: I'm not sure about mocking a protected method, but it'll do for now
|
|
$cluster = $this->getMock('Predis\Connection\Aggregate\RedisCluster', array('getRandomConnection'), array($factory));
|
|
$cluster->expects($this->exactly(2))
|
|
->method('getRandomConnection')
|
|
->will($this->onConsecutiveCalls($connection1, $connection2));
|
|
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
$cluster->add($connection3);
|
|
|
|
$cluster->setRetryLimit(1);
|
|
|
|
$cluster->askSlotsMap();
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testSupportsKeyHashTags()
|
|
{
|
|
$commands = $this->getCommandFactory();
|
|
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
|
|
$set = $commands->createCommand('set', array('{node:1001}:foo', 'foobar'));
|
|
$get = $commands->createCommand('get', array('{node:1001}:foo'));
|
|
$this->assertSame($connection1, $cluster->getConnection($set));
|
|
$this->assertSame($connection1, $cluster->getConnection($get));
|
|
|
|
$set = $commands->createCommand('set', array('{node:1001}:bar', 'foobar'));
|
|
$get = $commands->createCommand('get', array('{node:1001}:bar'));
|
|
$this->assertSame($connection1, $cluster->getConnection($set));
|
|
$this->assertSame($connection1, $cluster->getConnection($get));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testAskResponseWithConnectionInPool()
|
|
{
|
|
$askResponse = new Response\Error('ASK 1970 127.0.0.1:6380');
|
|
|
|
$command = $this->getCommandFactory()->createCommand('get', array('node:1001'));
|
|
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection1->expects($this->exactly(2))
|
|
->method('executeCommand')
|
|
->with($command)
|
|
->will($this->onConsecutiveCalls($askResponse, 'foobar'));
|
|
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
$connection2->expects($this->at(2))
|
|
->method('executeCommand')
|
|
->with($this->isRedisCommand('ASKING'));
|
|
$connection2->expects($this->at(3))
|
|
->method('executeCommand')
|
|
->with($command)
|
|
->will($this->returnValue('foobar'));
|
|
|
|
$factory = $this->getMock('Predis\Connection\Factory');
|
|
$factory->expects($this->never())->method('create');
|
|
|
|
$cluster = new RedisCluster($factory);
|
|
$cluster->useClusterSlots(false);
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
|
|
$this->assertSame('foobar', $cluster->executeCommand($command));
|
|
$this->assertSame('foobar', $cluster->executeCommand($command));
|
|
$this->assertSame(2, count($cluster));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testAskResponseWithConnectionNotInPool()
|
|
{
|
|
$askResponse = new Response\Error('ASK 1970 127.0.0.1:6381');
|
|
|
|
$command = $this->getCommandFactory()->createCommand('get', array('node:1001'));
|
|
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection1->expects($this->exactly(2))
|
|
->method('executeCommand')
|
|
->with($command)
|
|
->will($this->onConsecutiveCalls($askResponse, 'foobar'));
|
|
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
$connection2->expects($this->never())
|
|
->method('executeCommand');
|
|
|
|
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6381');
|
|
$connection3->expects($this->at(0))
|
|
->method('executeCommand')
|
|
->with($this->isRedisCommand('ASKING'));
|
|
$connection3->expects($this->at(1))
|
|
->method('executeCommand')
|
|
->with($command)
|
|
->will($this->returnValue('foobar'));
|
|
|
|
$factory = $this->getMock('Predis\Connection\Factory');
|
|
$factory->expects($this->once())
|
|
->method('create')
|
|
->with(array('host' => '127.0.0.1', 'port' => '6381'))
|
|
->will($this->returnValue($connection3));
|
|
|
|
$cluster = new RedisCluster($factory);
|
|
$cluster->useClusterSlots(false);
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
|
|
$this->assertSame('foobar', $cluster->executeCommand($command));
|
|
$this->assertSame('foobar', $cluster->executeCommand($command));
|
|
$this->assertSame(2, count($cluster));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testMovedResponseWithConnectionInPool()
|
|
{
|
|
$movedResponse = new Response\Error('MOVED 1970 127.0.0.1:6380');
|
|
|
|
$command = $this->getCommandFactory()->createCommand('get', array('node:1001'));
|
|
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection1->expects($this->exactly(1))
|
|
->method('executeCommand')
|
|
->with($command)
|
|
->will($this->returnValue($movedResponse));
|
|
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
$connection2->expects($this->exactly(2))
|
|
->method('executeCommand')
|
|
->with($command)
|
|
->will($this->onConsecutiveCalls('foobar', 'foobar'));
|
|
|
|
$factory = $this->getMock('Predis\Connection\Factory');
|
|
$factory->expects($this->never())->method('create');
|
|
|
|
$cluster = new RedisCluster($factory);
|
|
$cluster->useClusterSlots(false);
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
|
|
$this->assertSame('foobar', $cluster->executeCommand($command));
|
|
$this->assertSame('foobar', $cluster->executeCommand($command));
|
|
$this->assertSame(2, count($cluster));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testMovedResponseWithConnectionNotInPool()
|
|
{
|
|
$movedResponse = new Response\Error('MOVED 1970 127.0.0.1:6381');
|
|
|
|
$command = $this->getCommandFactory()->createCommand('get', array('node:1001'));
|
|
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection1->expects($this->once())
|
|
->method('executeCommand')
|
|
->with($command)
|
|
->will($this->returnValue($movedResponse));
|
|
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
$connection2->expects($this->never())
|
|
->method('executeCommand');
|
|
|
|
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6381');
|
|
$connection3->expects($this->exactly(2))
|
|
->method('executeCommand')
|
|
->with($command)
|
|
->will($this->onConsecutiveCalls('foobar', 'foobar'));
|
|
|
|
$factory = $this->getMock('Predis\Connection\Factory');
|
|
$factory->expects($this->once())
|
|
->method('create')
|
|
->with(array('host' => '127.0.0.1', 'port' => '6381'))
|
|
->will($this->returnValue($connection3));
|
|
|
|
$cluster = new RedisCluster($factory);
|
|
$cluster->useClusterSlots(false);
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
|
|
$this->assertSame('foobar', $cluster->executeCommand($command));
|
|
$this->assertSame('foobar', $cluster->executeCommand($command));
|
|
$this->assertSame(3, count($cluster));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testParseIPv6AddresseAndPortPairInRedirectionPayload()
|
|
{
|
|
$movedResponse = new Response\Error('MOVED 1970 2001:db8:0:f101::2:6379');
|
|
|
|
$command = $this->getCommandFactory()->createCommand('get', array('node:1001'));
|
|
|
|
$connection1 = $this->getMockConnection('tcp://[2001:db8:0:f101::1]:6379');
|
|
$connection1->expects($this->once())
|
|
->method('executeCommand')
|
|
->with($command)
|
|
->will($this->returnValue($movedResponse));
|
|
|
|
$connection2 = $this->getMockConnection('tcp://[2001:db8:0:f101::2]:6379');
|
|
$connection2->expects($this->once())
|
|
->method('executeCommand')
|
|
->with($command)
|
|
->will($this->returnValue('foobar'));
|
|
|
|
$factory = $this->getMock('Predis\Connection\Factory');
|
|
$factory->expects($this->once())
|
|
->method('create')
|
|
->with(array('host' => '2001:db8:0:f101::2', 'port' => '6379'))
|
|
->will($this->returnValue($connection2));
|
|
|
|
$cluster = new RedisCluster($factory);
|
|
$cluster->useClusterSlots(false);
|
|
$cluster->add($connection1);
|
|
|
|
$cluster->executeCommand($command);
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testFetchSlotsMapFromClusterWithClusterSlotsCommand()
|
|
{
|
|
$response = array(
|
|
array(12288, 13311, array('10.1.0.51', 6387), array('10.1.0.52', 6387)),
|
|
array(3072, 4095, array('10.1.0.52', 6392), array('10.1.0.51', 6392)),
|
|
array(6144, 7167, array('', 6384), array('10.1.0.52', 6384)),
|
|
array(14336, 15359, array('10.1.0.51', 6388), array('10.1.0.52', 6388)),
|
|
array(15360, 16383, array('10.1.0.52', 6398), array('10.1.0.51', 6398)),
|
|
array(1024, 2047, array('10.1.0.52', 6391), array('10.1.0.51', 6391)),
|
|
array(11264, 12287, array('10.1.0.52', 6396), array('10.1.0.51', 6396)),
|
|
array(5120, 6143, array('10.1.0.52', 6393), array('10.1.0.51', 6393)),
|
|
array(0, 1023, array('10.1.0.51', 6381), array('10.1.0.52', 6381)),
|
|
array(13312, 14335, array('10.1.0.52', 6397), array('10.1.0.51', 6397)),
|
|
array(4096, 5119, array('10.1.0.51', 6383), array('10.1.0.52', 6383)),
|
|
array(9216, 10239, array('10.1.0.52', 6395), array('10.1.0.51', 6395)),
|
|
array(8192, 9215, array('10.1.0.51', 6385), array('10.1.0.52', 6385)),
|
|
array(10240, 11263, array('10.1.0.51', 6386), array('10.1.0.52', 6386)),
|
|
array(2048, 3071, array('10.1.0.51', 6382), array('10.1.0.52', 6382)),
|
|
array(7168, 8191, array('10.1.0.52', 6394), array('10.1.0.51', 6394)),
|
|
);
|
|
|
|
$command = Command\RawCommand::create('CLUSTER', 'SLOTS');
|
|
|
|
$connection1 = $this->getMockConnection('tcp://10.1.0.51:6384');
|
|
$connection1->expects($this->once())
|
|
->method('executeCommand')
|
|
->with($command)
|
|
->will($this->returnValue($response));
|
|
|
|
$factory = $this->getMock('Predis\Connection\Factory');
|
|
|
|
$cluster = new RedisCluster($factory);
|
|
$cluster->add($connection1);
|
|
|
|
$cluster->askSlotsMap();
|
|
|
|
$this->assertSame($cluster->getConnectionBySlot('6144'), $connection1);
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testAskSlotsMapToRedisClusterOnMovedResponseByDefault()
|
|
{
|
|
$cmdGET = Command\RawCommand::create('GET', 'node:1001');
|
|
$rspMOVED = new Response\Error('MOVED 1970 127.0.0.1:6380');
|
|
$rspSlotsArray = array(
|
|
array(0, 8191, array('127.0.0.1', 6379)),
|
|
array(8192, 16383, array('127.0.0.1', 6380)),
|
|
);
|
|
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
|
$connection1->expects($this->once())
|
|
->method('executeCommand')
|
|
->with($cmdGET)
|
|
->will($this->returnValue($rspMOVED));
|
|
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
|
$connection2->expects($this->at(0))
|
|
->method('executeCommand')
|
|
->with($this->isRedisCommand('CLUSTER', array('SLOTS')))
|
|
->will($this->returnValue($rspSlotsArray));
|
|
$connection2->expects($this->at(2))
|
|
->method('executeCommand')
|
|
->with($cmdGET)
|
|
->will($this->returnValue('foobar'));
|
|
|
|
$factory = $this->getMock('Predis\Connection\Factory');
|
|
$factory->expects($this->once())
|
|
->method('create')
|
|
->with(array('host' => '127.0.0.1', 'port' => '6380'))
|
|
->will($this->returnValue($connection2));
|
|
|
|
$cluster = new RedisCluster($factory);
|
|
$cluster->add($connection1);
|
|
|
|
$this->assertSame('foobar', $cluster->executeCommand($cmdGET));
|
|
$this->assertSame(2, count($cluster));
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
* @expectedException \Predis\NotSupportedException
|
|
* @expectedExceptionMessage Cannot use 'PING' with redis-cluster.
|
|
*/
|
|
public function testThrowsExceptionOnNonSupportedCommand()
|
|
{
|
|
$ping = $this->getCommandFactory()->createCommand('ping');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($this->getMockConnection('tcp://127.0.0.1:6379'));
|
|
|
|
$cluster->getConnection($ping);
|
|
}
|
|
|
|
/**
|
|
* @medium
|
|
* @group disconnected
|
|
*/
|
|
public function testCanBeSerialized()
|
|
{
|
|
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379?slots=0-1364');
|
|
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380?slots=1365-2729');
|
|
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=2730-4095');
|
|
|
|
$cluster = new RedisCluster(new Connection\Factory());
|
|
$cluster->add($connection1);
|
|
$cluster->add($connection2);
|
|
$cluster->add($connection3);
|
|
|
|
$cluster->buildSlotsMap();
|
|
|
|
$unserialized = unserialize(serialize($cluster));
|
|
|
|
$this->assertEquals($cluster, $unserialized);
|
|
}
|
|
}
|