mirror of
https://github.com/predis/predis.git
synced 2026-08-31 12:43:31 +00:00
Extract slot map logic to separate class.
In addition to that, the methods askSlotsMap() and buildSlotsMap() of the redis-cluster connection backend have been renamed respectively to askSlotMap() and buildSlotMap().
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
<?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\Cluster;
|
||||
|
||||
/**
|
||||
* Slot map for redis-cluster.
|
||||
*/
|
||||
class SlotMap implements \ArrayAccess, \IteratorAggregate, \Countable
|
||||
{
|
||||
private $slots = array();
|
||||
|
||||
/**
|
||||
* Checks if the given slot is valid.
|
||||
*
|
||||
* @param int $first Slot index.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValid($slot)
|
||||
{
|
||||
return $slot >= 0x0000 && $slot <= 0x3FFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given slot range is valid.
|
||||
*
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidRange($first, $last)
|
||||
{
|
||||
return $first >= 0x0000 && $first <= 0x3FFF && $last >= 0x0000 && $last <= 0x3FFF && $first <= $last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the slot map.
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->slots = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the slot map is empty.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return empty($this->slots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current slot map as a dictionary of $slot => $node.
|
||||
*
|
||||
* The order of the slots in the dictionary is not guaranteed.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of unique nodes in the slot map.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getNodes()
|
||||
{
|
||||
return array_keys(array_flip($this->slots));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the specified slot range to a node.
|
||||
*
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
* @param NodeConnectionInterface|string $connection ID or connection instance.
|
||||
*
|
||||
* @throws \OutOfBoundsException
|
||||
*/
|
||||
public function setSlots($first, $last, $connection)
|
||||
{
|
||||
if (!static::isValidRange($first, $last)) {
|
||||
throw new \OutOfBoundsException("Invalid slot range $first-$last for `$connection`");
|
||||
}
|
||||
|
||||
$this->slots += array_fill($first, $last - $first + 1, (string) $connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified slot range.
|
||||
*
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSlots($first, $last)
|
||||
{
|
||||
if (!static::isValidRange($first, $last)) {
|
||||
throw new \OutOfBoundsException("Invalid slot range $first-$last");
|
||||
}
|
||||
|
||||
return array_intersect_key($this->slots, array_fill($first, $last - $first + 1, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified slot is assigned.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($slot)
|
||||
{
|
||||
return isset($this->slots[$slot]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node assigned to the specified slot
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function offsetGet($slot)
|
||||
{
|
||||
if (isset($this->slots[$slot])) {
|
||||
return $this->slots[$slot];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the specified slot to a node.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
* @param NodeConnectionInterface|string $connection ID or connection instance.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function offsetSet($slot, $connection)
|
||||
{
|
||||
if (!static::isValid($slot)) {
|
||||
throw new \OutOfBoundsException("Invalid slot $slot for `$connection`");
|
||||
}
|
||||
|
||||
$this->slots[(int) $slot] = (string) $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node assigned to the specified slot
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function offsetUnset($slot)
|
||||
{
|
||||
unset($this->slots[$slot]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current number of assigned slots.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->slots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the slot map.
|
||||
*
|
||||
* @return \ArrayIterator
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->slots);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ namespace Predis\Connection\Cluster;
|
||||
|
||||
use Predis\ClientException;
|
||||
use Predis\Cluster\RedisStrategy as RedisClusterStrategy;
|
||||
use Predis\Cluster\SlotMap;
|
||||
use Predis\Cluster\StrategyInterface;
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Command\RawCommand;
|
||||
@@ -49,7 +50,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
private $useClusterSlots = true;
|
||||
private $pool = array();
|
||||
private $slots = array();
|
||||
private $slotsMap;
|
||||
private $slotmap;
|
||||
private $strategy;
|
||||
private $connections;
|
||||
private $retryLimit = 5;
|
||||
@@ -64,6 +65,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
) {
|
||||
$this->connections = $connections;
|
||||
$this->strategy = $strategy ?: new RedisClusterStrategy();
|
||||
$this->slotmap = new SlotMap();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,7 +122,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
public function add(NodeConnectionInterface $connection)
|
||||
{
|
||||
$this->pool[(string) $connection] = $connection;
|
||||
unset($this->slotsMap);
|
||||
$this->slotmap->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,12 +131,9 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
public function remove(NodeConnectionInterface $connection)
|
||||
{
|
||||
if (false !== $id = array_search($connection, $this->pool, true)) {
|
||||
unset(
|
||||
$this->pool[$id],
|
||||
$this->slotsMap
|
||||
);
|
||||
|
||||
$this->slotmap->reset();
|
||||
$this->slots = array_diff($this->slots, array($connection));
|
||||
unset($this->pool[$id]);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -152,10 +151,9 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
public function removeById($connectionID)
|
||||
{
|
||||
if (isset($this->pool[$connectionID])) {
|
||||
unset(
|
||||
$this->pool[$connectionID],
|
||||
$this->slotsMap
|
||||
);
|
||||
$this->slotmap->reset();
|
||||
$this->slots = array_diff($this->slots, array($connectionID));
|
||||
unset($this->pool[$connectionID]);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -171,12 +169,10 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
* cluster, so it is most effective when all of the connections supplied on
|
||||
* initialization have the "slots" parameter properly set accordingly to the
|
||||
* current cluster configuration.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function buildSlotsMap()
|
||||
public function buildSlotMap()
|
||||
{
|
||||
$this->slotsMap = array();
|
||||
$this->slotmap->reset();
|
||||
|
||||
foreach ($this->pool as $connectionID => $connection) {
|
||||
$parameters = $connection->getParameters();
|
||||
@@ -192,11 +188,9 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
$slots[1] = $slots[0];
|
||||
}
|
||||
|
||||
$this->setSlots($slots[0], $slots[1], $connectionID);
|
||||
$this->slotmap->setSlots($slots[0], $slots[1], $connectionID);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->slotsMap;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,7 +204,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function queryClusterNodeForSlotsMap(NodeConnectionInterface $connection)
|
||||
private function queryClusterNodeForSlotMap(NodeConnectionInterface $connection)
|
||||
{
|
||||
$retries = 0;
|
||||
$command = RawCommand::create('CLUSTER', 'SLOTS');
|
||||
@@ -246,18 +240,16 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
* the pool.
|
||||
*
|
||||
* @param NodeConnectionInterface $connection Optional connection instance.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function askSlotsMap(NodeConnectionInterface $connection = null)
|
||||
public function askSlotMap(NodeConnectionInterface $connection = null)
|
||||
{
|
||||
if (!$connection && !$connection = $this->getRandomConnection()) {
|
||||
return array();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->resetSlotsMap();
|
||||
$this->slotmap->reset();
|
||||
|
||||
$response = $this->queryClusterNodeForSlotsMap($connection);
|
||||
$response = $this->queryClusterNodeForSlotMap($connection);
|
||||
|
||||
foreach ($response as $slots) {
|
||||
// We only support master servers for now, so we ignore subsequent
|
||||
@@ -265,61 +257,11 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
list($start, $end, $master) = $slots;
|
||||
|
||||
if ($master[0] === '') {
|
||||
$this->setSlots($start, $end, (string) $connection);
|
||||
$this->slotmap->setSlots($start, $end, (string) $connection);
|
||||
} else {
|
||||
$this->setSlots($start, $end, "{$master[0]}:{$master[1]}");
|
||||
$this->slotmap->setSlots($start, $end, "{$master[0]}:{$master[1]}");
|
||||
}
|
||||
}
|
||||
|
||||
return $this->slotsMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the slots map cache.
|
||||
*/
|
||||
public function resetSlotsMap()
|
||||
{
|
||||
$this->slotsMap = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current slots map for the cluster.
|
||||
*
|
||||
* The order of the returned $slot => $server dictionary is not guaranteed.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSlotsMap()
|
||||
{
|
||||
if (!isset($this->slotsMap)) {
|
||||
$this->slotsMap = array();
|
||||
}
|
||||
|
||||
return $this->slotsMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-associates a connection to a slots range to avoid runtime guessing.
|
||||
*
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
* @param NodeConnectionInterface|string $connection ID or connection instance.
|
||||
*
|
||||
* @throws \OutOfBoundsException
|
||||
*/
|
||||
public function setSlots($first, $last, $connection)
|
||||
{
|
||||
if ($first < 0x0000 || $first > 0x3FFF ||
|
||||
$last < 0x0000 || $last > 0x3FFF ||
|
||||
$last < $first
|
||||
) {
|
||||
throw new \OutOfBoundsException(
|
||||
"Invalid slot range for $connection: [$first-$last]."
|
||||
);
|
||||
}
|
||||
|
||||
$slots = array_fill($first, $last - $first + 1, (string) $connection);
|
||||
$this->slotsMap = $this->getSlotsMap() + $slots;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,12 +279,12 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
throw new ClientException('No connections available in the pool');
|
||||
}
|
||||
|
||||
if (!isset($this->slotsMap)) {
|
||||
$this->buildSlotsMap();
|
||||
if ($this->slotmap->isEmpty()) {
|
||||
$this->buildSlotMap();
|
||||
}
|
||||
|
||||
if (isset($this->slotsMap[$slot])) {
|
||||
return $this->slotsMap[$slot];
|
||||
if ($node = $this->slotmap[$slot]) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
$count = count($this->pool);
|
||||
@@ -400,7 +342,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
*/
|
||||
public function getConnectionBySlot($slot)
|
||||
{
|
||||
if ($slot < 0x0000 || $slot > 0x3FFF) {
|
||||
if (!SlotMap::isValid($slot)) {
|
||||
throw new \OutOfBoundsException("Invalid slot [$slot].");
|
||||
}
|
||||
|
||||
@@ -451,6 +393,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
{
|
||||
$this->pool[(string) $connection] = $connection;
|
||||
$this->slots[(int) $slot] = $connection;
|
||||
$this->slotmap[(int) $slot] = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -495,7 +438,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
}
|
||||
|
||||
if ($this->useClusterSlots) {
|
||||
$this->askSlotsMap($connection);
|
||||
$this->askSlotMap($connection);
|
||||
}
|
||||
|
||||
$this->move($connection, $slot);
|
||||
@@ -557,7 +500,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
if ($failure) {
|
||||
throw $exception;
|
||||
} elseif ($this->useClusterSlots) {
|
||||
$this->askSlotsMap();
|
||||
$this->askSlotMap();
|
||||
}
|
||||
|
||||
$failure = true;
|
||||
@@ -612,15 +555,13 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
if ($this->useClusterSlots) {
|
||||
$slotsmap = $this->getSlotsMap() ?: $this->askSlotsMap();
|
||||
} else {
|
||||
$slotsmap = $this->getSlotsMap() ?: $this->buildSlotsMap();
|
||||
if ($this->slotmap->isEmpty()) {
|
||||
$this->useClusterSlots ? $this->askSlotMap() : $this->buildSlotMap();
|
||||
}
|
||||
|
||||
$connections = array();
|
||||
|
||||
foreach (array_unique($slotsmap) as $node) {
|
||||
foreach ($this->slotmap->getNodes() as $node) {
|
||||
if (!$connection = $this->getConnectionById($node)) {
|
||||
$this->add($connection = $this->createConnection($node));
|
||||
}
|
||||
@@ -631,6 +572,16 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
return new \ArrayIterator($connections);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying slot map.
|
||||
*
|
||||
* @return SlotMap
|
||||
*/
|
||||
public function getSlotMap()
|
||||
{
|
||||
return $this->slotmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying command hash strategy used to hash commands by
|
||||
* using keys found in their arguments.
|
||||
@@ -661,7 +612,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
* procedure, mostly when targeting many keys that would end up in a lot of
|
||||
* redirections.
|
||||
*
|
||||
* The slots map can still be manually fetched using the askSlotsMap()
|
||||
* The slots map can still be manually fetched using the askSlotMap()
|
||||
* method whether or not this option is enabled.
|
||||
*
|
||||
* @param bool $value Enable or disable the use of CLUSTER SLOTS.
|
||||
|
||||
@@ -0,0 +1,471 @@
|
||||
<?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\Cluster;
|
||||
|
||||
use PredisTestCase;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class SlotMapTest extends PredisTestCase
|
||||
{
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsValidReturnsTrueOnValidSlot()
|
||||
{
|
||||
$this->assertTrue(SlotMap::isValid(0));
|
||||
$this->assertTrue(SlotMap::isValid(16383));
|
||||
|
||||
$this->assertTrue(SlotMap::isValid(5000));
|
||||
$this->assertTrue(SlotMap::isValid('5000'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsValidReturnsFalseOnInvalidSlot()
|
||||
{
|
||||
$this->assertFalse(SlotMap::isValid(-1));
|
||||
$this->assertFalse(SlotMap::isValid(16384));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsValidRangeReturnsTrueOnValidSlotRange()
|
||||
{
|
||||
$this->assertTrue(SlotMap::isValidRange(0, 16383));
|
||||
$this->assertTrue(SlotMap::isValidRange(2000, 2999));
|
||||
$this->assertTrue(SlotMap::isValidRange(3000, 3000));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsValidRangeReturnsFalseOnInvalidSlotRange()
|
||||
{
|
||||
$this->assertFalse(SlotMap::isValidRange(0, 16384));
|
||||
$this->assertFalse(SlotMap::isValidRange(-1, 16383));
|
||||
$this->assertFalse(SlotMap::isValidRange(-1, 16384));
|
||||
$this->assertFalse(SlotMap::isValidRange(2999, 2000));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testToArrayReturnsEmptyArrayOnEmptySlotMap()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$this->assertEmpty($slotmap->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testSetSlotsAssignsSpecifiedNodeToSlotRange()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(5461, 10921, '127.0.0.1:6380');
|
||||
$slotmap->setSlots(10922, 16383, '127.0.0.1:6381');
|
||||
|
||||
$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')
|
||||
);
|
||||
|
||||
$this->assertSame($expectedMap, $slotmap->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testSetSlotsOverwritesSlotRange()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(1000, 2000, '127.0.0.1:6380');
|
||||
|
||||
$expectedMap =
|
||||
array_fill(0, 5461, '127.0.0.1:6379') +
|
||||
array_fill(1000, 2000, '127.0.0.1:6380');
|
||||
|
||||
$this->assertSame($expectedMap, $slotmap->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testSetSlotsAssignsSingleSlotWhenFirstAndLastSlotMatch()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(10, 10, '127.0.0.1:6379');
|
||||
|
||||
$this->assertSame(array(10 => '127.0.0.1:6379'), $slotmap->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testSetSlotsCastsValueToString()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$connection = $this->getMockConnection();
|
||||
$connection
|
||||
->expects($this->once())
|
||||
->method('__toString')
|
||||
->will($this->returnValue('127.0.0.1:6379'));
|
||||
|
||||
$slotmap->setSlots(10, 10, $connection);
|
||||
|
||||
$this->assertSame(array(10 => '127.0.0.1:6379'), $slotmap->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
* @expectedException \OutOfBoundsException
|
||||
* @expectedExceptionMessage Invalid slot range 0-16384 for `127.0.0.1:6379`
|
||||
*/
|
||||
public function testSetSlotsThrowsExceptionOnInvalidSlotRange()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 16384, '127.0.0.1:6379');
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetSlotsReturnsEmptyArrayOnEmptySlotMap()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$this->assertEmpty($slotmap->getSlots(3, 11));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetSlotsReturnsDictionaryOfSlotsWithAssignedNodes()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(10, 13, '127.0.0.1:6380');
|
||||
|
||||
$expectedMap = array(
|
||||
3 => '127.0.0.1:6379',
|
||||
4 => '127.0.0.1:6379',
|
||||
5 => '127.0.0.1:6379',
|
||||
10 => '127.0.0.1:6380',
|
||||
11 => '127.0.0.1:6380',
|
||||
);
|
||||
|
||||
$this->assertSame($expectedMap, $slotmap->getSlots(3, 11));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetSlotsReturnsEmptyArrayOnEmptySlotRange()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(10, 13, '127.0.0.1:6380');
|
||||
|
||||
$this->assertEmpty($slotmap->getSlots(100, 200));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
* @expectedException \OutOfBoundsException
|
||||
* @expectedExceptionMessage Invalid slot range 0-16384
|
||||
*/
|
||||
public function testGetSlotsThrowsExceptionOnInvalidSlotRange()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->getSlots(0, 16384);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsEmptyReturnsTrueOnEmptySlotMap()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$this->assertTrue($slotmap->isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsEmptyReturnsFalseOnNonEmptySlotMap()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertFalse($slotmap->isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testCountReturnsZeroOnEmptySlotMap()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$this->assertSame(0, count($slotmap));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testCountReturnsAssignedSlotsInSlotMap()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$this->assertSame(5461, count($slotmap));
|
||||
|
||||
$slotmap->setSlots(5461, 10921, '127.0.0.1:6380');
|
||||
$this->assertSame(10922, count($slotmap));
|
||||
|
||||
$slotmap->setSlots(10922, 16383, '127.0.0.1:6381');
|
||||
$this->assertSame(16384, count($slotmap));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testResetEmptiesSlotMap()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(5461, 10921, '127.0.0.1:6380');
|
||||
$slotmap->setSlots(10922, 16383, '127.0.0.1:6381');
|
||||
|
||||
$this->assertFalse($slotmap->isEmpty());
|
||||
|
||||
$slotmap->reset();
|
||||
|
||||
$this->assertTrue($slotmap->isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetNodesReturnsEmptyArrayOnEmptySlotMap()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$this->assertEmpty($slotmap->getNodes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetNodesReturnsArrayOfNodesInSlotMap()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(5461, 10921, '127.0.0.1:6380');
|
||||
$slotmap->setSlots(10922, 16383, '127.0.0.1:6381');
|
||||
|
||||
$this->assertSame(array('127.0.0.1:6379', '127.0.0.1:6380', '127.0.0.1:6381'), $slotmap->getNodes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetExistsReturnsTrueOnAssignedSlot()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertTrue(isset($slotmap[0]));
|
||||
$this->assertTrue(isset($slotmap[2000]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetExistsReturnsFalseOnAssignedSlot()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertFalse(isset($slotmap[6000]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetExistsReturnsFalseOnInvalidSlot()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertFalse(isset($slotmap[-100]));
|
||||
$this->assertFalse(isset($slotmap[16384]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetGetReturnsNodeOfAssignedSlot()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(5461, 10921, '127.0.0.1:6380');
|
||||
$slotmap->setSlots(10922, 16383, '127.0.0.1:6381');
|
||||
|
||||
$this->assertSame('127.0.0.1:6379', $slotmap[0]);
|
||||
$this->assertSame('127.0.0.1:6380', $slotmap[5461]);
|
||||
$this->assertSame('127.0.0.1:6381', $slotmap[10922]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetGetReturnsNullOnUnassignedSlot()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertNull($slotmap[5461]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetGetReturnsNullOnInvalidSlot()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertNull($slotmap[-100]);
|
||||
$this->assertNull($slotmap[16384]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetUnsetRemovesSlotAssignment()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertTrue(isset($slotmap[100]));
|
||||
unset($slotmap[100]);
|
||||
$this->assertFalse(isset($slotmap[100]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetUnsetDoesNotDoAnythingOnUnassignedSlot()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertFalse(isset($slotmap[5461]));
|
||||
unset($slotmap[5461]);
|
||||
$this->assertFalse(isset($slotmap[5461]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetSetAssignsNodeToSlot()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertSame('127.0.0.1:6380', $slotmap[100] = '127.0.0.1:6380');
|
||||
$this->assertSame('127.0.0.1:6380', $slotmap[100]);
|
||||
|
||||
$this->assertNull($slotmap[5461]);
|
||||
$this->assertSame('127.0.0.1:6380', $slotmap[5461] = '127.0.0.1:6380');
|
||||
$this->assertSame('127.0.0.1:6380', $slotmap[5461]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetSetCastsValueToString()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$connection = $this->getMockConnection();
|
||||
$connection
|
||||
->expects($this->once())
|
||||
->method('__toString')
|
||||
->will($this->returnValue('127.0.0.1:6379'));
|
||||
|
||||
$this->assertSame($connection, $slotmap[0] = $connection);
|
||||
$this->assertSame('127.0.0.1:6379', $slotmap[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
* @expectedException \OutOfBoundsException
|
||||
* @expectedExceptionMessage Invalid slot 16384 for `127.0.0.1:6379`
|
||||
*/
|
||||
public function testOffsetSetThrowsExceptionOnInvalidSlot()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap[16384] = '127.0.0.1:6379';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetIteratorReturnsIteratorOverSlotMap()
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(5461, 10921, '127.0.0.1:6380');
|
||||
$slotmap->setSlots(10922, 16383, '127.0.0.1:6381');
|
||||
|
||||
$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')
|
||||
);
|
||||
|
||||
$this->assertSame($expectedMap, iterator_to_array($slotmap));
|
||||
}
|
||||
}
|
||||
@@ -348,34 +348,6 @@ class RedisClusterTest extends PredisTestCase
|
||||
$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
|
||||
*/
|
||||
@@ -388,12 +360,14 @@ class RedisClusterTest extends PredisTestCase
|
||||
|
||||
$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());
|
||||
$slotmap = $cluster->getSlotMap();
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertSame(array_fill(0, 5461, '127.0.0.1:6379'), $slotmap->toArray());
|
||||
|
||||
$cluster->add($connection2);
|
||||
|
||||
$this->assertEmpty($cluster->getSlotsMap());
|
||||
$this->assertCount(0, $slotmap);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -409,18 +383,20 @@ class RedisClusterTest extends PredisTestCase
|
||||
$cluster->add($connection1);
|
||||
$cluster->add($connection2);
|
||||
|
||||
$cluster->setSlots(0, 2047, '127.0.0.1:6379');
|
||||
$cluster->setSlots(2048, 4095, '127.0.0.1:6380');
|
||||
$slotmap = $cluster->getSlotMap();
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(5461, 10921, '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')
|
||||
array_fill(0, 5461, '127.0.0.1:6379'),
|
||||
array_fill(5460, 5461, '127.0.0.1:6380')
|
||||
);
|
||||
|
||||
$this->assertSame($expectedMap, $cluster->getSlotsMap());
|
||||
$this->assertSame($expectedMap, $slotmap->toArray());
|
||||
|
||||
$cluster->remove($connection1);
|
||||
$this->assertEmpty($cluster->getSlotsMap());
|
||||
|
||||
$this->assertCount(0, $slotmap);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -438,7 +414,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
$cluster->add($connection2);
|
||||
$cluster->add($connection3);
|
||||
|
||||
$cluster->buildSlotsMap();
|
||||
$cluster->buildSlotMap();
|
||||
|
||||
$expectedMap = array_merge(
|
||||
array_fill(0, 5461, '127.0.0.1:6379'),
|
||||
@@ -446,7 +422,8 @@ class RedisClusterTest extends PredisTestCase
|
||||
array_fill(10921, 5462, '127.0.0.1:6381')
|
||||
);
|
||||
|
||||
$actualMap = $cluster->getSlotsMap();
|
||||
$actualMap = $cluster->getSlotMap()->toArray();
|
||||
|
||||
ksort($actualMap);
|
||||
|
||||
$this->assertSame($expectedMap, $actualMap);
|
||||
@@ -467,7 +444,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
$cluster->add($connection2);
|
||||
$cluster->add($connection3);
|
||||
|
||||
$cluster->buildSlotsMap();
|
||||
$cluster->buildSlotMap();
|
||||
|
||||
$expectedMap = array_merge(
|
||||
array_fill(0, 5461, '127.0.0.1:6379'),
|
||||
@@ -479,7 +456,8 @@ class RedisClusterTest extends PredisTestCase
|
||||
array_fill(11000, 5383, '127.0.0.1:6381')
|
||||
);
|
||||
|
||||
$actualMap = $cluster->getSlotsMap();
|
||||
$actualMap = $cluster->getSlotMap()->toArray();
|
||||
|
||||
ksort($actualMap);
|
||||
|
||||
$this->assertSame($expectedMap, $actualMap);
|
||||
@@ -504,7 +482,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
$this->assertSame($connection2, $cluster->getConnectionBySlot(5461));
|
||||
$this->assertSame($connection3, $cluster->getConnectionBySlot(10922));
|
||||
|
||||
$cluster->setSlots(5461, 7096, '127.0.0.1:6380');
|
||||
$cluster->getSlotMap()->setSlots(5461, 7096, '127.0.0.1:6380');
|
||||
$this->assertSame($connection2, $cluster->getConnectionBySlot(5461));
|
||||
}
|
||||
|
||||
@@ -679,7 +657,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testRetriesExecutingCommandOnConnectionFailureButDoNotAskSlotsMapWhenDisabled()
|
||||
public function testRetriesExecutingCommandOnConnectionFailureButDoNotAskSlotMapWhenDisabled()
|
||||
{
|
||||
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5500');
|
||||
$connection1
|
||||
@@ -766,7 +744,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testAskSlotsMapReturnEmptyArrayOnEmptyConnectionsPool()
|
||||
public function testAskSlotMapReturnEmptyArrayOnEmptyConnectionsPool()
|
||||
{
|
||||
$factory = $this->getMock('Predis\Connection\FactoryInterface');
|
||||
$factory
|
||||
@@ -774,14 +752,15 @@ class RedisClusterTest extends PredisTestCase
|
||||
->method('create');
|
||||
|
||||
$cluster = new RedisCluster($factory);
|
||||
$cluster->askSlotMap();
|
||||
|
||||
$this->assertEmpty($cluster->askSlotsMap());
|
||||
$this->assertCount(0, $cluster->getSlotMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testAskSlotsMapRetriesOnDifferentNodeOnConnectionFailure()
|
||||
public function testAskSlotMapRetriesOnDifferentNodeOnConnectionFailure()
|
||||
{
|
||||
$slotsmap = array(
|
||||
array(0, 5460, array('127.0.0.1', 9381), array()),
|
||||
@@ -836,7 +815,9 @@ class RedisClusterTest extends PredisTestCase
|
||||
$cluster->add($connection2);
|
||||
$cluster->add($connection3);
|
||||
|
||||
$this->assertCount(16384, $cluster->askSlotsMap());
|
||||
$cluster->askSlotMap();
|
||||
|
||||
$this->assertCount(16384, $cluster->getSlotMap());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -844,7 +825,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
* @expectedException \Predis\Connection\ConnectionException
|
||||
* @expectedExceptionMessage Unknown connection error [127.0.0.1:6382]
|
||||
*/
|
||||
public function testAskSlotsMapHonorsRetryLimitOnMultipleConnectionFailures()
|
||||
public function testAskSlotMapHonorsRetryLimitOnMultipleConnectionFailures()
|
||||
{
|
||||
$slotsmap = array(
|
||||
array(0, 5460, array('127.0.0.1', 9381), array()),
|
||||
@@ -897,7 +878,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
|
||||
$cluster->setRetryLimit(1);
|
||||
|
||||
$cluster->askSlotsMap();
|
||||
$cluster->askSlotMap();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1190,7 +1171,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
|
||||
$cluster->add($connection1);
|
||||
|
||||
$cluster->askSlotsMap();
|
||||
$cluster->askSlotMap();
|
||||
|
||||
$this->assertSame($cluster->getConnectionBySlot('6144'), $connection1);
|
||||
}
|
||||
@@ -1198,7 +1179,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testAskSlotsMapToRedisClusterOnMovedResponseByDefault()
|
||||
public function testAskSlotMapToRedisClusterOnMovedResponseByDefault()
|
||||
{
|
||||
$cmdGET = Command\RawCommand::create('GET', 'node:1001');
|
||||
$rspMOVED = new Response\Error('MOVED 1970 127.0.0.1:6380');
|
||||
@@ -1223,7 +1204,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
))
|
||||
->will($this->returnValue($rspSlotsArray));
|
||||
$connection2
|
||||
->expects($this->at(2))
|
||||
->expects($this->at(3))
|
||||
->method('executeCommand')
|
||||
->with($cmdGET)
|
||||
->will($this->returnValue('foobar'));
|
||||
@@ -1278,7 +1259,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
$cluster->add($connection2);
|
||||
$cluster->add($connection3);
|
||||
|
||||
$cluster->buildSlotsMap();
|
||||
$cluster->buildSlotMap();
|
||||
|
||||
$unserialized = unserialize(serialize($cluster));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user