RedisClusterTest.php 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287
  1. <?php
  2. /*
  3. * This file is part of the Predis package.
  4. *
  5. * (c) Daniele Alessandri <suppakilla@gmail.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Predis\Connection\Cluster;
  11. use Predis\Command;
  12. use Predis\Connection;
  13. use Predis\Response;
  14. use PredisTestCase;
  15. /**
  16. *
  17. */
  18. class RedisClusterTest extends PredisTestCase
  19. {
  20. /**
  21. * @group disconnected
  22. */
  23. public function testAcceptsCustomConnectionFactory()
  24. {
  25. $factory = $this->getMock('Predis\Connection\FactoryInterface');
  26. $cluster = new RedisCluster($factory);
  27. $this->assertSame($factory, $cluster->getConnectionFactory());
  28. }
  29. /**
  30. * @group disconnected
  31. */
  32. public function testUsesRedisClusterStrategyByDefault()
  33. {
  34. $cluster = new RedisCluster(new Connection\Factory());
  35. $this->assertInstanceOf('Predis\Cluster\RedisStrategy', $cluster->getClusterStrategy());
  36. }
  37. /**
  38. * @group disconnected
  39. */
  40. public function testAcceptsCustomClusterStrategy()
  41. {
  42. $strategy = $this->getMock('Predis\Cluster\StrategyInterface');
  43. $cluster = new RedisCluster(new Connection\Factory(), $strategy);
  44. $this->assertSame($strategy, $cluster->getClusterStrategy());
  45. }
  46. /**
  47. * @group disconnected
  48. */
  49. public function testAddingConnectionsToCluster()
  50. {
  51. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  52. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  53. $cluster = new RedisCluster(new Connection\Factory());
  54. $cluster->add($connection1);
  55. $cluster->add($connection2);
  56. $this->assertSame(2, count($cluster));
  57. $this->assertSame($connection1, $cluster->getConnectionById('127.0.0.1:6379'));
  58. $this->assertSame($connection2, $cluster->getConnectionById('127.0.0.1:6380'));
  59. }
  60. /**
  61. * @group disconnected
  62. */
  63. public function testRemovingConnectionsFromCluster()
  64. {
  65. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  66. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  67. $connection3 = $this->getMockConnection('tcp://127.0.0.1:6371');
  68. $cluster = new RedisCluster(new Connection\Factory());
  69. $cluster->add($connection1);
  70. $cluster->add($connection2);
  71. $this->assertTrue($cluster->remove($connection1));
  72. $this->assertFalse($cluster->remove($connection3));
  73. $this->assertSame(1, count($cluster));
  74. }
  75. /**
  76. * @group disconnected
  77. */
  78. public function testRemovingConnectionsFromClusterByAlias()
  79. {
  80. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  81. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  82. $cluster = new RedisCluster(new Connection\Factory());
  83. $cluster->add($connection1);
  84. $cluster->add($connection2);
  85. $this->assertTrue($cluster->removeById('127.0.0.1:6380'));
  86. $this->assertFalse($cluster->removeById('127.0.0.1:6390'));
  87. $this->assertSame(1, count($cluster));
  88. }
  89. /**
  90. * @group disconnected
  91. */
  92. public function testCountReturnsNumberOfConnectionsInPool()
  93. {
  94. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  95. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  96. $connection3 = $this->getMockConnection('tcp://127.0.0.1:6381');
  97. $cluster = new RedisCluster(new Connection\Factory());
  98. $cluster->add($connection1);
  99. $cluster->add($connection2);
  100. $cluster->add($connection3);
  101. $this->assertSame(3, count($cluster));
  102. $cluster->remove($connection3);
  103. $this->assertSame(2, count($cluster));
  104. }
  105. /**
  106. * @group disconnected
  107. */
  108. public function testConnectPicksRandomConnection()
  109. {
  110. $connect1 = false;
  111. $connect2 = false;
  112. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  113. $connection1
  114. ->expects($this->any())
  115. ->method('connect')
  116. ->will($this->returnCallback(function () use (&$connect1) {
  117. $connect1 = true;
  118. }));
  119. $connection1
  120. ->expects($this->any())
  121. ->method('isConnected')
  122. ->will($this->returnCallback(function () use (&$connect1) {
  123. return $connect1;
  124. }));
  125. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  126. $connection2
  127. ->expects($this->any())
  128. ->method('connect')
  129. ->will($this->returnCallback(function () use (&$connect2) {
  130. $connect2 = true;
  131. }));
  132. $connection2
  133. ->expects($this->any())
  134. ->method('isConnected')
  135. ->will($this->returnCallback(function () use (&$connect2) {
  136. return $connect2;
  137. }));
  138. $cluster = new RedisCluster(new Connection\Factory());
  139. $cluster->add($connection1);
  140. $cluster->add($connection2);
  141. $cluster->connect();
  142. $this->assertTrue($cluster->isConnected());
  143. if ($connect1) {
  144. $this->assertTrue($connect1);
  145. $this->assertFalse($connect2);
  146. } else {
  147. $this->assertFalse($connect1);
  148. $this->assertTrue($connect2);
  149. }
  150. }
  151. /**
  152. * @group disconnected
  153. */
  154. public function testDisconnectForcesAllConnectionsToDisconnect()
  155. {
  156. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  157. $connection1
  158. ->expects($this->once())
  159. ->method('disconnect');
  160. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  161. $connection2
  162. ->expects($this->once())
  163. ->method('disconnect');
  164. $cluster = new RedisCluster(new Connection\Factory());
  165. $cluster->add($connection1);
  166. $cluster->add($connection2);
  167. $cluster->disconnect();
  168. }
  169. /**
  170. * @group disconnected
  171. */
  172. public function testIsConnectedReturnsTrueIfAtLeastOneConnectionIsOpen()
  173. {
  174. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  175. $connection1
  176. ->expects($this->once())
  177. ->method('isConnected')
  178. ->will($this->returnValue(false));
  179. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  180. $connection2
  181. ->expects($this->once())
  182. ->method('isConnected')
  183. ->will($this->returnValue(true));
  184. $cluster = new RedisCluster(new Connection\Factory());
  185. $cluster->add($connection1);
  186. $cluster->add($connection2);
  187. $this->assertTrue($cluster->isConnected());
  188. }
  189. /**
  190. * @group disconnected
  191. */
  192. public function testIsConnectedReturnsFalseIfAllConnectionsAreClosed()
  193. {
  194. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  195. $connection1
  196. ->expects($this->once())
  197. ->method('isConnected')
  198. ->will($this->returnValue(false));
  199. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  200. $connection2
  201. ->expects($this->once())
  202. ->method('isConnected')
  203. ->will($this->returnValue(false));
  204. $cluster = new RedisCluster(new Connection\Factory());
  205. $cluster->add($connection1);
  206. $cluster->add($connection2);
  207. $this->assertFalse($cluster->isConnected());
  208. }
  209. /**
  210. * @group disconnected
  211. */
  212. public function testGetIteratorReturnsConnectionsMappedInSlotsMapWhenUseClusterSlotsIsDisabled()
  213. {
  214. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5460');
  215. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10921');
  216. $connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10922-16383');
  217. $connection4 = $this->getMockConnection('tcp://127.0.0.1:6384');
  218. $cluster = new RedisCluster(new Connection\Factory());
  219. $cluster->useClusterSlots(false);
  220. $cluster->add($connection1);
  221. $cluster->add($connection2);
  222. $cluster->add($connection3);
  223. $cluster->add($connection4);
  224. $this->assertInstanceOf('Iterator', $iterator = $cluster->getIterator());
  225. $connections = iterator_to_array($iterator);
  226. $this->assertCount(3, $connections);
  227. $this->assertSame($connection1, $connections[0]);
  228. $this->assertSame($connection2, $connections[1]);
  229. $this->assertSame($connection3, $connections[2]);
  230. }
  231. /**
  232. * @group disconnected
  233. */
  234. public function testGetIteratorReturnsConnectionsMappedInSlotsMapFetchedFromRedisCluster()
  235. {
  236. $slotsmap = array(
  237. array(0, 5460, array('127.0.0.1', 6381), array()),
  238. array(5461, 10921, array('127.0.0.1', 6383), array()),
  239. array(10922, 16383, array('127.0.0.1', 6384), array()),
  240. );
  241. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5460');
  242. $connection1
  243. ->expects($this->once())
  244. ->method('executeCommand')
  245. ->with($this->isRedisCommand(
  246. 'CLUSTER', array('SLOTS')
  247. ))
  248. ->will($this->returnValue($slotsmap));
  249. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10921');
  250. $connection3 = $this->getMockConnection('tcp://127.0.0.1:6383');
  251. $connection4 = $this->getMockConnection('tcp://127.0.0.1:6384');
  252. $factory = $this->getMock('Predis\Connection\FactoryInterface');
  253. $factory
  254. ->expects($this->at(0))
  255. ->method('create')
  256. ->with(array(
  257. 'host' => '127.0.0.1',
  258. 'port' => '6383',
  259. ))
  260. ->will($this->returnValue($connection3));
  261. $factory
  262. ->expects($this->at(1))
  263. ->method('create')
  264. ->with(array(
  265. 'host' => '127.0.0.1',
  266. 'port' => '6384',
  267. ))
  268. ->will($this->returnValue($connection4));
  269. // TODO: I'm not sure about mocking a protected method, but it'll do for now
  270. $cluster = $this->getMock('Predis\Connection\Cluster\RedisCluster', array('getRandomConnection'), array($factory));
  271. $cluster
  272. ->expects($this->once())
  273. ->method('getRandomConnection')
  274. ->will($this->returnValue($connection1));
  275. $cluster->add($connection1);
  276. $cluster->add($connection2);
  277. $cluster->useClusterSlots(true);
  278. $this->assertInstanceOf('Iterator', $iterator = $cluster->getIterator());
  279. $connections = iterator_to_array($iterator);
  280. $this->assertCount(3, $connections);
  281. $this->assertSame($connection1, $connections[0]);
  282. $this->assertSame($connection3, $connections[1]);
  283. $this->assertSame($connection4, $connections[2]);
  284. }
  285. /**
  286. * @group disconnected
  287. */
  288. public function testCanAssignConnectionsToCustomSlots()
  289. {
  290. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  291. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  292. $connection3 = $this->getMockConnection('tcp://127.0.0.1:6381');
  293. $cluster = new RedisCluster(new Connection\Factory());
  294. $cluster->add($connection1);
  295. $cluster->add($connection2);
  296. $cluster->add($connection3);
  297. $cluster->setSlots(0, 1364, '127.0.0.1:6379');
  298. $cluster->setSlots(1365, 2729, '127.0.0.1:6380');
  299. $cluster->setSlots(2730, 4095, '127.0.0.1:6381');
  300. $expectedMap = array_merge(
  301. array_fill(0, 1365, '127.0.0.1:6379'),
  302. array_fill(1364, 1365, '127.0.0.1:6380'),
  303. array_fill(2729, 1366, '127.0.0.1:6381')
  304. );
  305. $this->assertSame($expectedMap, $cluster->getSlotsMap());
  306. }
  307. /**
  308. * @group disconnected
  309. */
  310. public function testAddingConnectionResetsSlotsMap()
  311. {
  312. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  313. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  314. $cluster = new RedisCluster(new Connection\Factory());
  315. $cluster->add($connection1);
  316. $cluster->setSlots(0, 4095, '127.0.0.1:6379');
  317. $this->assertSame(array_fill(0, 4096, '127.0.0.1:6379'), $cluster->getSlotsMap());
  318. $cluster->add($connection2);
  319. $this->assertEmpty($cluster->getSlotsMap());
  320. }
  321. /**
  322. * @group disconnected
  323. */
  324. public function testRemovingConnectionResetsSlotsMap()
  325. {
  326. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  327. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  328. $cluster = new RedisCluster(new Connection\Factory());
  329. $cluster->add($connection1);
  330. $cluster->add($connection2);
  331. $cluster->setSlots(0, 2047, '127.0.0.1:6379');
  332. $cluster->setSlots(2048, 4095, '127.0.0.1:6380');
  333. $expectedMap = array_merge(
  334. array_fill(0, 2048, '127.0.0.1:6379'),
  335. array_fill(2048, 2048, '127.0.0.1:6380')
  336. );
  337. $this->assertSame($expectedMap, $cluster->getSlotsMap());
  338. $cluster->remove($connection1);
  339. $this->assertEmpty($cluster->getSlotsMap());
  340. }
  341. /**
  342. * @group disconnected
  343. */
  344. public function testCanAssignConnectionsToRangeOfSlotsFromParameters()
  345. {
  346. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379?slots=0-5460');
  347. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380?slots=5461-10921');
  348. $connection3 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=10922-16383');
  349. $cluster = new RedisCluster(new Connection\Factory());
  350. $cluster->add($connection1);
  351. $cluster->add($connection2);
  352. $cluster->add($connection3);
  353. $cluster->buildSlotsMap();
  354. $expectedMap = array_merge(
  355. array_fill(0, 5461, '127.0.0.1:6379'),
  356. array_fill(5460, 5461, '127.0.0.1:6380'),
  357. array_fill(10921, 5462, '127.0.0.1:6381')
  358. );
  359. $actualMap = $cluster->getSlotsMap();
  360. ksort($actualMap);
  361. $this->assertSame($expectedMap, $actualMap);
  362. }
  363. /**
  364. * @group disconnected
  365. */
  366. public function testCanAssignConnectionsToSingleSlotOrRangesOfSlotsFromParameters()
  367. {
  368. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379?slots=0-5460,5500-5600,11000');
  369. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380?slots=5461-5499,5600-10921');
  370. $connection3 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=10922-10999,11001-16383');
  371. $cluster = new RedisCluster(new Connection\Factory());
  372. $cluster->add($connection1);
  373. $cluster->add($connection2);
  374. $cluster->add($connection3);
  375. $cluster->buildSlotsMap();
  376. $expectedMap = array_merge(
  377. array_fill(0, 5461, '127.0.0.1:6379'),
  378. array_fill(5460, 39, '127.0.0.1:6380'),
  379. array_fill(5499, 101, '127.0.0.1:6379'),
  380. array_fill(5599, 5321, '127.0.0.1:6380'),
  381. array_fill(10921, 78, '127.0.0.1:6381'),
  382. array_fill(11000, 1, '127.0.0.1:6379'),
  383. array_fill(11000, 5383, '127.0.0.1:6381')
  384. );
  385. $actualMap = $cluster->getSlotsMap();
  386. ksort($actualMap);
  387. $this->assertSame($expectedMap, $actualMap);
  388. }
  389. /**
  390. * @group disconnected
  391. */
  392. public function testReturnsCorrectConnectionUsingSlotID()
  393. {
  394. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  395. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  396. $connection3 = $this->getMockConnection('tcp://127.0.0.1:6381');
  397. $cluster = new RedisCluster(new Connection\Factory());
  398. $cluster->add($connection1);
  399. $cluster->add($connection2);
  400. $cluster->add($connection3);
  401. $this->assertSame($connection1, $cluster->getConnectionBySlot(0));
  402. $this->assertSame($connection2, $cluster->getConnectionBySlot(5461));
  403. $this->assertSame($connection3, $cluster->getConnectionBySlot(10922));
  404. $cluster->setSlots(5461, 7096, '127.0.0.1:6380');
  405. $this->assertSame($connection2, $cluster->getConnectionBySlot(5461));
  406. }
  407. /**
  408. * @group disconnected
  409. */
  410. public function testReturnsCorrectConnectionUsingCommandInstance()
  411. {
  412. $commands = $this->getCommandFactory();
  413. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  414. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  415. $connection3 = $this->getMockConnection('tcp://127.0.0.1:6381');
  416. $cluster = new RedisCluster(new Connection\Factory());
  417. $cluster->add($connection1);
  418. $cluster->add($connection2);
  419. $cluster->add($connection3);
  420. $set = $commands->createCommand('set', array('node:1001', 'foobar'));
  421. $get = $commands->createCommand('get', array('node:1001'));
  422. $this->assertSame($connection1, $cluster->getConnection($set));
  423. $this->assertSame($connection1, $cluster->getConnection($get));
  424. $set = $commands->createCommand('set', array('node:1048', 'foobar'));
  425. $get = $commands->createCommand('get', array('node:1048'));
  426. $this->assertSame($connection2, $cluster->getConnection($set));
  427. $this->assertSame($connection2, $cluster->getConnection($get));
  428. $set = $commands->createCommand('set', array('node:1082', 'foobar'));
  429. $get = $commands->createCommand('get', array('node:1082'));
  430. $this->assertSame($connection3, $cluster->getConnection($set));
  431. $this->assertSame($connection3, $cluster->getConnection($get));
  432. }
  433. /**
  434. * @group disconnected
  435. */
  436. public function testWritesCommandToCorrectConnection()
  437. {
  438. $command = $this->getCommandFactory()->createCommand('get', array('node:1001'));
  439. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  440. $connection1
  441. ->expects($this->once())
  442. ->method('writeRequest')
  443. ->with($command);
  444. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  445. $connection2
  446. ->expects($this->never())
  447. ->method('writeRequest');
  448. $cluster = new RedisCluster(new Connection\Factory());
  449. $cluster->useClusterSlots(false);
  450. $cluster->add($connection1);
  451. $cluster->add($connection2);
  452. $cluster->writeRequest($command);
  453. }
  454. /**
  455. * @group disconnected
  456. */
  457. public function testReadsCommandFromCorrectConnection()
  458. {
  459. $command = $this->getCommandFactory()->createCommand('get', array('node:1050'));
  460. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  461. $connection1
  462. ->expects($this->never())
  463. ->method('readResponse');
  464. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  465. $connection2
  466. ->expects($this->once())
  467. ->method('readResponse')
  468. ->with($command);
  469. $cluster = new RedisCluster(new Connection\Factory());
  470. $cluster->useClusterSlots(false);
  471. $cluster->add($connection1);
  472. $cluster->add($connection2);
  473. $cluster->readResponse($command);
  474. }
  475. /**
  476. * @group disconnected
  477. */
  478. public function testRetriesExecutingCommandOnConnectionFailureOnlyAfterFetchingNewSlotsMap()
  479. {
  480. $slotsmap = array(
  481. array(0, 5460, array('127.0.0.1', 9381), array()),
  482. array(5461, 10921, array('127.0.0.1', 6382), array()),
  483. array(10922, 16383, array('127.0.0.1', 6383), array()),
  484. );
  485. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5460');
  486. $connection1
  487. ->expects($this->once())
  488. ->method('executeCommand')
  489. ->with($this->isRedisCommand(
  490. 'GET', array('node:1001')
  491. ))
  492. ->will($this->throwException(
  493. new Connection\ConnectionException($connection1, 'Unknown connection error [127.0.0.1:6381]')
  494. ));
  495. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10921');
  496. $connection2
  497. ->expects($this->any())
  498. ->method('executeCommand')
  499. ->with($this->isRedisCommand(
  500. 'CLUSTER', array('SLOTS')
  501. ))
  502. ->will($this->returnValue($slotsmap));
  503. $connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10922-16383');
  504. $connection3
  505. ->expects($this->any())
  506. ->method('executeCommand')
  507. ->with($this->isRedisCommand(
  508. 'CLUSTER', array('SLOTS')
  509. ))
  510. ->will($this->returnValue($slotsmap));
  511. $connection4 = $this->getMockConnection('tcp://127.0.0.1:9381');
  512. $connection4
  513. ->expects($this->at(0))
  514. ->method('executeCommand')
  515. ->with($this->isRedisCommand(
  516. 'GET', array('node:1001')
  517. ))
  518. ->will($this->returnValue('value:1001'));
  519. $connection4
  520. ->expects($this->at(1))
  521. ->method('executeCommand')
  522. ->with($this->isRedisCommand(
  523. 'GET', array('node:5001')
  524. ))
  525. ->will($this->returnValue('value:5001'));
  526. $factory = $this->getMock('Predis\Connection\FactoryInterface');
  527. $factory
  528. ->expects($this->once())
  529. ->method('create')
  530. ->with(array(
  531. 'host' => '127.0.0.1',
  532. 'port' => '9381',
  533. ))
  534. ->will($this->returnValue($connection4));
  535. $cluster = new RedisCluster($factory);
  536. $cluster->add($connection1);
  537. $cluster->add($connection2);
  538. $cluster->add($connection3);
  539. $this->assertSame('value:1001', $cluster->executeCommand(
  540. Command\RawCommand::create('get', 'node:1001')
  541. ));
  542. $this->assertSame('value:5001', $cluster->executeCommand(
  543. Command\RawCommand::create('get', 'node:5001')
  544. ));
  545. }
  546. /**
  547. * @group disconnected
  548. */
  549. public function testRetriesExecutingCommandOnConnectionFailureButDoNotAskSlotsMapWhenDisabled()
  550. {
  551. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5500');
  552. $connection1
  553. ->expects($this->once())
  554. ->method('executeCommand')
  555. ->with($this->isRedisCommand(
  556. 'GET', array('node:1001')
  557. ))
  558. ->will($this->throwException(
  559. new Connection\ConnectionException($connection1, 'Unknown connection error [127.0.0.1:6381]')
  560. ));
  561. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5501-11000');
  562. $connection2
  563. ->expects($this->once())
  564. ->method('executeCommand')
  565. ->with($this->isRedisCommand(
  566. 'GET', array('node:1001')
  567. ))
  568. ->will($this->returnValue(
  569. new Response\Error('MOVED 1970 127.0.0.1:9381')
  570. ));
  571. $connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=11101-16383');
  572. $connection3
  573. ->expects($this->never())
  574. ->method('executeCommand');
  575. $connection4 = $this->getMockConnection('tcp://127.0.0.1:9381');
  576. $connection4
  577. ->expects($this->once())
  578. ->method('executeCommand')
  579. ->with($this->isRedisCommand(
  580. 'GET', array('node:1001')
  581. ))
  582. ->will($this->returnValue('value:1001'));
  583. $factory = $this->getMock('Predis\Connection\FactoryInterface');
  584. $factory
  585. ->expects($this->once())
  586. ->method('create')
  587. ->with(array(
  588. 'host' => '127.0.0.1',
  589. 'port' => '9381',
  590. ))
  591. ->will($this->returnValue($connection4));
  592. // TODO: I'm not sure about mocking a protected method, but it'll do for now
  593. $cluster = $this->getMock('Predis\Connection\Cluster\RedisCluster', array('getRandomConnection'), array($factory));
  594. $cluster
  595. ->expects($this->never())
  596. ->method('getRandomConnection');
  597. $cluster->useClusterSlots(false);
  598. $cluster->add($connection1);
  599. $cluster->add($connection2);
  600. $cluster->add($connection3);
  601. $this->assertSame('value:1001', $cluster->executeCommand(
  602. Command\RawCommand::create('get', 'node:1001')
  603. ));
  604. }
  605. /**
  606. * @group disconnected
  607. * @expectedException \Predis\ClientException
  608. * @expectedExceptionMessage No connections available in the pool
  609. */
  610. public function testThrowsClientExceptionWhenExecutingCommandWithEmptyPool()
  611. {
  612. $factory = $this->getMock('Predis\Connection\FactoryInterface');
  613. $factory
  614. ->expects($this->never())
  615. ->method('create');
  616. $cluster = new RedisCluster($factory);
  617. $cluster->executeCommand(
  618. Command\RawCommand::create('get', 'node:1001')
  619. );
  620. }
  621. /**
  622. * @group disconnected
  623. */
  624. public function testAskSlotsMapReturnEmptyArrayOnEmptyConnectionsPool()
  625. {
  626. $factory = $this->getMock('Predis\Connection\FactoryInterface');
  627. $factory
  628. ->expects($this->never())
  629. ->method('create');
  630. $cluster = new RedisCluster($factory);
  631. $this->assertEmpty($cluster->askSlotsMap());
  632. }
  633. /**
  634. * @group disconnected
  635. */
  636. public function testAskSlotsMapRetriesOnDifferentNodeOnConnectionFailure()
  637. {
  638. $slotsmap = array(
  639. array(0, 5460, array('127.0.0.1', 9381), array()),
  640. array(5461, 10921, array('127.0.0.1', 6382), array()),
  641. array(10922, 16383, array('127.0.0.1', 6383), array()),
  642. );
  643. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5460');
  644. $connection1
  645. ->expects($this->once())
  646. ->method('executeCommand')
  647. ->with($this->isRedisCommand(
  648. 'CLUSTER', array('SLOTS')
  649. ))
  650. ->will($this->throwException(
  651. new Connection\ConnectionException($connection1, 'Unknown connection error [127.0.0.1:6381]')
  652. ));
  653. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10921');
  654. $connection2
  655. ->expects($this->once())
  656. ->method('executeCommand')
  657. ->with($this->isRedisCommand(
  658. 'CLUSTER', array('SLOTS')
  659. ))
  660. ->will($this->throwException(
  661. new Connection\ConnectionException($connection2, 'Unknown connection error [127.0.0.1:6383]')
  662. ));
  663. $connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10922-16383');
  664. $connection3
  665. ->expects($this->once())
  666. ->method('executeCommand')
  667. ->with($this->isRedisCommand(
  668. 'CLUSTER', array('SLOTS')
  669. ))
  670. ->will($this->returnValue($slotsmap));
  671. $factory = $this->getMock('Predis\Connection\FactoryInterface');
  672. $factory
  673. ->expects($this->never())
  674. ->method('create');
  675. // TODO: I'm not sure about mocking a protected method, but it'll do for now
  676. $cluster = $this->getMock('Predis\Connection\Cluster\RedisCluster', array('getRandomConnection'), array($factory));
  677. $cluster
  678. ->expects($this->exactly(3))
  679. ->method('getRandomConnection')
  680. ->will($this->onConsecutiveCalls($connection1, $connection2, $connection3));
  681. $cluster->add($connection1);
  682. $cluster->add($connection2);
  683. $cluster->add($connection3);
  684. $this->assertCount(16384, $cluster->askSlotsMap());
  685. }
  686. /**
  687. * @group disconnected
  688. * @expectedException \Predis\Connection\ConnectionException
  689. * @expectedExceptionMessage Unknown connection error [127.0.0.1:6382]
  690. */
  691. public function testAskSlotsMapHonorsRetryLimitOnMultipleConnectionFailures()
  692. {
  693. $slotsmap = array(
  694. array(0, 5460, array('127.0.0.1', 9381), array()),
  695. array(5461, 10921, array('127.0.0.1', 6382), array()),
  696. array(10922, 16383, array('127.0.0.1', 6383), array()),
  697. );
  698. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5460');
  699. $connection1
  700. ->expects($this->any())
  701. ->method('executeCommand')
  702. ->with($this->isRedisCommand(
  703. 'CLUSTER', array('SLOTS')
  704. ))
  705. ->will($this->throwException(
  706. new Connection\ConnectionException($connection1, 'Unknown connection error [127.0.0.1:6381]')
  707. ));
  708. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10921');
  709. $connection2
  710. ->expects($this->any())
  711. ->method('executeCommand')
  712. ->with($this->isRedisCommand(
  713. 'CLUSTER', array('SLOTS')
  714. ))
  715. ->will($this->throwException(
  716. new Connection\ConnectionException($connection2, 'Unknown connection error [127.0.0.1:6382]')
  717. ));
  718. $connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10922-16383');
  719. $connection3
  720. ->expects($this->never())
  721. ->method('executeCommand');
  722. $factory = $this->getMock('Predis\Connection\FactoryInterface');
  723. $factory
  724. ->expects($this->never())
  725. ->method('create');
  726. // TODO: I'm not sure about mocking a protected method, but it'll do for now
  727. $cluster = $this->getMock('Predis\Connection\Cluster\RedisCluster', array('getRandomConnection'), array($factory));
  728. $cluster
  729. ->expects($this->exactly(2))
  730. ->method('getRandomConnection')
  731. ->will($this->onConsecutiveCalls($connection1, $connection2));
  732. $cluster->add($connection1);
  733. $cluster->add($connection2);
  734. $cluster->add($connection3);
  735. $cluster->setRetryLimit(1);
  736. $cluster->askSlotsMap();
  737. }
  738. /**
  739. * @group disconnected
  740. */
  741. public function testSupportsKeyHashTags()
  742. {
  743. $commands = $this->getCommandFactory();
  744. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  745. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  746. $cluster = new RedisCluster(new Connection\Factory());
  747. $cluster->add($connection1);
  748. $cluster->add($connection2);
  749. $set = $commands->createCommand('set', array('{node:1001}:foo', 'foobar'));
  750. $get = $commands->createCommand('get', array('{node:1001}:foo'));
  751. $this->assertSame($connection1, $cluster->getConnection($set));
  752. $this->assertSame($connection1, $cluster->getConnection($get));
  753. $set = $commands->createCommand('set', array('{node:1001}:bar', 'foobar'));
  754. $get = $commands->createCommand('get', array('{node:1001}:bar'));
  755. $this->assertSame($connection1, $cluster->getConnection($set));
  756. $this->assertSame($connection1, $cluster->getConnection($get));
  757. }
  758. /**
  759. * @group disconnected
  760. */
  761. public function testAskResponseWithConnectionInPool()
  762. {
  763. $askResponse = new Response\Error('ASK 1970 127.0.0.1:6380');
  764. $command = $this->getCommandFactory()->createCommand('get', array('node:1001'));
  765. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  766. $connection1
  767. ->expects($this->exactly(2))
  768. ->method('executeCommand')
  769. ->with($command)
  770. ->will($this->onConsecutiveCalls($askResponse, 'foobar'));
  771. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  772. $connection2
  773. ->expects($this->at(2))
  774. ->method('executeCommand')
  775. ->with($this->isRedisCommand('ASKING'));
  776. $connection2
  777. ->expects($this->at(3))
  778. ->method('executeCommand')
  779. ->with($command)
  780. ->will($this->returnValue('foobar'));
  781. $factory = $this->getMock('Predis\Connection\Factory');
  782. $factory
  783. ->expects($this->never())
  784. ->method('create');
  785. $cluster = new RedisCluster($factory);
  786. $cluster->useClusterSlots(false);
  787. $cluster->add($connection1);
  788. $cluster->add($connection2);
  789. $this->assertSame('foobar', $cluster->executeCommand($command));
  790. $this->assertSame('foobar', $cluster->executeCommand($command));
  791. $this->assertSame(2, count($cluster));
  792. }
  793. /**
  794. * @group disconnected
  795. */
  796. public function testAskResponseWithConnectionNotInPool()
  797. {
  798. $askResponse = new Response\Error('ASK 1970 127.0.0.1:6381');
  799. $command = $this->getCommandFactory()->createCommand('get', array('node:1001'));
  800. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  801. $connection1
  802. ->expects($this->exactly(2))
  803. ->method('executeCommand')
  804. ->with($command)
  805. ->will($this->onConsecutiveCalls($askResponse, 'foobar'));
  806. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  807. $connection2
  808. ->expects($this->never())
  809. ->method('executeCommand');
  810. $connection3 = $this->getMockConnection('tcp://127.0.0.1:6381');
  811. $connection3
  812. ->expects($this->at(0))
  813. ->method('executeCommand')
  814. ->with($this->isRedisCommand(
  815. 'ASKING'
  816. ));
  817. $connection3
  818. ->expects($this->at(1))
  819. ->method('executeCommand')
  820. ->with($command)
  821. ->will($this->returnValue('foobar'));
  822. $factory = $this->getMock('Predis\Connection\Factory');
  823. $factory
  824. ->expects($this->once())
  825. ->method('create')
  826. ->with(array(
  827. 'host' => '127.0.0.1',
  828. 'port' => '6381',
  829. ))
  830. ->will($this->returnValue($connection3));
  831. $cluster = new RedisCluster($factory);
  832. $cluster->useClusterSlots(false);
  833. $cluster->add($connection1);
  834. $cluster->add($connection2);
  835. $this->assertSame('foobar', $cluster->executeCommand($command));
  836. $this->assertSame('foobar', $cluster->executeCommand($command));
  837. $this->assertSame(2, count($cluster));
  838. }
  839. /**
  840. * @group disconnected
  841. */
  842. public function testMovedResponseWithConnectionInPool()
  843. {
  844. $movedResponse = new Response\Error('MOVED 1970 127.0.0.1:6380');
  845. $command = $this->getCommandFactory()->createCommand('get', array('node:1001'));
  846. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  847. $connection1
  848. ->expects($this->exactly(1))
  849. ->method('executeCommand')
  850. ->with($command)
  851. ->will($this->returnValue($movedResponse));
  852. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  853. $connection2
  854. ->expects($this->exactly(2))
  855. ->method('executeCommand')
  856. ->with($command)
  857. ->will($this->onConsecutiveCalls('foobar', 'foobar'));
  858. $factory = $this->getMock('Predis\Connection\Factory');
  859. $factory->expects($this->never())->method('create');
  860. $cluster = new RedisCluster($factory);
  861. $cluster->useClusterSlots(false);
  862. $cluster->add($connection1);
  863. $cluster->add($connection2);
  864. $this->assertSame('foobar', $cluster->executeCommand($command));
  865. $this->assertSame('foobar', $cluster->executeCommand($command));
  866. $this->assertSame(2, count($cluster));
  867. }
  868. /**
  869. * @group disconnected
  870. */
  871. public function testMovedResponseWithConnectionNotInPool()
  872. {
  873. $movedResponse = new Response\Error('MOVED 1970 127.0.0.1:6381');
  874. $command = $this->getCommandFactory()->createCommand('get', array('node:1001'));
  875. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  876. $connection1
  877. ->expects($this->once())
  878. ->method('executeCommand')
  879. ->with($command)
  880. ->will($this->returnValue($movedResponse));
  881. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  882. $connection2
  883. ->expects($this->never())
  884. ->method('executeCommand');
  885. $connection3 = $this->getMockConnection('tcp://127.0.0.1:6381');
  886. $connection3
  887. ->expects($this->exactly(2))
  888. ->method('executeCommand')
  889. ->with($command)
  890. ->will($this->onConsecutiveCalls('foobar', 'foobar'));
  891. $factory = $this->getMock('Predis\Connection\Factory');
  892. $factory
  893. ->expects($this->once())
  894. ->method('create')
  895. ->with(array(
  896. 'host' => '127.0.0.1',
  897. 'port' => '6381',
  898. ))
  899. ->will($this->returnValue($connection3));
  900. $cluster = new RedisCluster($factory);
  901. $cluster->useClusterSlots(false);
  902. $cluster->add($connection1);
  903. $cluster->add($connection2);
  904. $this->assertSame('foobar', $cluster->executeCommand($command));
  905. $this->assertSame('foobar', $cluster->executeCommand($command));
  906. $this->assertSame(3, count($cluster));
  907. }
  908. /**
  909. * @group disconnected
  910. */
  911. public function testParseIPv6AddresseAndPortPairInRedirectionPayload()
  912. {
  913. $movedResponse = new Response\Error('MOVED 1970 2001:db8:0:f101::2:6379');
  914. $command = $this->getCommandFactory()->createCommand('get', array('node:1001'));
  915. $connection1 = $this->getMockConnection('tcp://[2001:db8:0:f101::1]:6379');
  916. $connection1
  917. ->expects($this->once())
  918. ->method('executeCommand')
  919. ->with($command)
  920. ->will($this->returnValue($movedResponse));
  921. $connection2 = $this->getMockConnection('tcp://[2001:db8:0:f101::2]:6379');
  922. $connection2
  923. ->expects($this->once())
  924. ->method('executeCommand')
  925. ->with($command)
  926. ->will($this->returnValue('foobar'));
  927. $factory = $this->getMock('Predis\Connection\Factory');
  928. $factory
  929. ->expects($this->once())
  930. ->method('create')
  931. ->with(array(
  932. 'host' => '2001:db8:0:f101::2',
  933. 'port' => '6379',
  934. ))
  935. ->will($this->returnValue($connection2));
  936. $cluster = new RedisCluster($factory);
  937. $cluster->useClusterSlots(false);
  938. $cluster->add($connection1);
  939. $cluster->executeCommand($command);
  940. }
  941. /**
  942. * @group disconnected
  943. */
  944. public function testFetchSlotsMapFromClusterWithClusterSlotsCommand()
  945. {
  946. $response = array(
  947. array(12288, 13311, array('10.1.0.51', 6387), array('10.1.0.52', 6387)),
  948. array(3072, 4095, array('10.1.0.52', 6392), array('10.1.0.51', 6392)),
  949. array(6144, 7167, array('', 6384), array('10.1.0.52', 6384)),
  950. array(14336, 15359, array('10.1.0.51', 6388), array('10.1.0.52', 6388)),
  951. array(15360, 16383, array('10.1.0.52', 6398), array('10.1.0.51', 6398)),
  952. array(1024, 2047, array('10.1.0.52', 6391), array('10.1.0.51', 6391)),
  953. array(11264, 12287, array('10.1.0.52', 6396), array('10.1.0.51', 6396)),
  954. array(5120, 6143, array('10.1.0.52', 6393), array('10.1.0.51', 6393)),
  955. array(0, 1023, array('10.1.0.51', 6381), array('10.1.0.52', 6381)),
  956. array(13312, 14335, array('10.1.0.52', 6397), array('10.1.0.51', 6397)),
  957. array(4096, 5119, array('10.1.0.51', 6383), array('10.1.0.52', 6383)),
  958. array(9216, 10239, array('10.1.0.52', 6395), array('10.1.0.51', 6395)),
  959. array(8192, 9215, array('10.1.0.51', 6385), array('10.1.0.52', 6385)),
  960. array(10240, 11263, array('10.1.0.51', 6386), array('10.1.0.52', 6386)),
  961. array(2048, 3071, array('10.1.0.51', 6382), array('10.1.0.52', 6382)),
  962. array(7168, 8191, array('10.1.0.52', 6394), array('10.1.0.51', 6394)),
  963. );
  964. $connection1 = $this->getMockConnection('tcp://10.1.0.51:6384');
  965. $connection1
  966. ->expects($this->once())
  967. ->method('executeCommand')
  968. ->with($this->isRedisCommand(
  969. 'CLUSTER', array('SLOTS')
  970. ))
  971. ->will($this->returnValue($response));
  972. $factory = $this->getMock('Predis\Connection\Factory');
  973. $cluster = new RedisCluster($factory);
  974. $cluster->add($connection1);
  975. $cluster->askSlotsMap();
  976. $this->assertSame($cluster->getConnectionBySlot('6144'), $connection1);
  977. }
  978. /**
  979. * @group disconnected
  980. */
  981. public function testAskSlotsMapToRedisClusterOnMovedResponseByDefault()
  982. {
  983. $cmdGET = Command\RawCommand::create('GET', 'node:1001');
  984. $rspMOVED = new Response\Error('MOVED 1970 127.0.0.1:6380');
  985. $rspSlotsArray = array(
  986. array(0, 8191, array('127.0.0.1', 6379)),
  987. array(8192, 16383, array('127.0.0.1', 6380)),
  988. );
  989. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
  990. $connection1
  991. ->expects($this->once())
  992. ->method('executeCommand')
  993. ->with($cmdGET)
  994. ->will($this->returnValue($rspMOVED));
  995. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
  996. $connection2
  997. ->expects($this->at(0))
  998. ->method('executeCommand')
  999. ->with($this->isRedisCommand(
  1000. 'CLUSTER', array('SLOTS')
  1001. ))
  1002. ->will($this->returnValue($rspSlotsArray));
  1003. $connection2
  1004. ->expects($this->at(2))
  1005. ->method('executeCommand')
  1006. ->with($cmdGET)
  1007. ->will($this->returnValue('foobar'));
  1008. $factory = $this->getMock('Predis\Connection\Factory');
  1009. $factory
  1010. ->expects($this->once())
  1011. ->method('create')
  1012. ->with(array(
  1013. 'host' => '127.0.0.1',
  1014. 'port' => '6380',
  1015. ))
  1016. ->will($this->returnValue($connection2));
  1017. $cluster = new RedisCluster($factory);
  1018. $cluster->add($connection1);
  1019. $this->assertSame('foobar', $cluster->executeCommand($cmdGET));
  1020. $this->assertSame(2, count($cluster));
  1021. }
  1022. /**
  1023. * @group disconnected
  1024. * @expectedException \Predis\NotSupportedException
  1025. * @expectedExceptionMessage Cannot use 'PING' with redis-cluster.
  1026. */
  1027. public function testThrowsExceptionOnNonSupportedCommand()
  1028. {
  1029. $ping = $this->getCommandFactory()->createCommand('ping');
  1030. $cluster = new RedisCluster(new Connection\Factory());
  1031. $cluster->add($this->getMockConnection('tcp://127.0.0.1:6379'));
  1032. $cluster->getConnection($ping);
  1033. }
  1034. /**
  1035. * @medium
  1036. * @group disconnected
  1037. */
  1038. public function testCanBeSerialized()
  1039. {
  1040. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6379?slots=0-1364');
  1041. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6380?slots=1365-2729');
  1042. $connection3 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=2730-4095');
  1043. $cluster = new RedisCluster(new Connection\Factory());
  1044. $cluster->add($connection1);
  1045. $cluster->add($connection2);
  1046. $cluster->add($connection3);
  1047. $cluster->buildSlotsMap();
  1048. $unserialized = unserialize(serialize($cluster));
  1049. $this->assertEquals($cluster, $unserialized);
  1050. }
  1051. }