SentinelReplicationTest.php 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444
  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\Replication;
  11. use Predis\Command;
  12. use Predis\Connection;
  13. use Predis\Replication;
  14. use Predis\Response;
  15. use PredisTestCase;
  16. /**
  17. *
  18. */
  19. class SentinelReplicationTest extends PredisTestCase
  20. {
  21. /**
  22. * @group disconnected
  23. * @expectedException Predis\ClientException
  24. * @expectedExceptionMessage No sentinel server available for autodiscovery.
  25. */
  26. public function testMethodGetSentinelConnectionThrowsExceptionOnEmptySentinelsPool()
  27. {
  28. $replication = $this->getReplicationConnection('svc', array());
  29. $replication->getSentinelConnection();
  30. }
  31. /**
  32. * @group disconnected
  33. */
  34. public function testParametersForSentinelConnectionShouldNotUseDatabaseAndPassword()
  35. {
  36. $replication = $this->getReplicationConnection('svc', array(
  37. 'tcp://127.0.0.1:5381?role=sentinel&database=1&password=secret',
  38. ));
  39. $parameters = $replication->getSentinelConnection()->getParameters()->toArray();
  40. $this->assertArraySubset(array('database' => null, 'password' => null), $parameters);
  41. }
  42. /**
  43. * @group disconnected
  44. */
  45. public function testParametersForSentinelConnectionHaveDefaultTimeout()
  46. {
  47. $replication = $this->getReplicationConnection('svc', array(
  48. 'tcp://127.0.0.1:5381?role=sentinel',
  49. ));
  50. $parameters = $replication->getSentinelConnection()->getParameters()->toArray();
  51. $this->assertArrayHasKey('timeout', $parameters);
  52. $this->assertSame(0.100, $parameters['timeout']);
  53. }
  54. /**
  55. * @group disconnected
  56. */
  57. public function testParametersForSentinelConnectionCanOverrideDefaultTimeout()
  58. {
  59. $replication = $this->getReplicationConnection('svc', array(
  60. 'tcp://127.0.0.1:5381?role=sentinel&timeout=1',
  61. ));
  62. $parameters = $replication
  63. ->getSentinelConnection()
  64. ->getParameters()
  65. ->toArray();
  66. $this->assertArrayHasKey('timeout', $parameters);
  67. $this->assertSame('1', $parameters['timeout']);
  68. }
  69. /**
  70. * @group disconnected
  71. */
  72. public function testConnectionParametersInstanceForSentinelConnectionIsNotModified()
  73. {
  74. $originalParameters = Connection\Parameters::create(
  75. 'tcp://127.0.0.1:5381?role=sentinel&database=1&password=secret'
  76. );
  77. $replication = $this->getReplicationConnection('svc', array($originalParameters));
  78. $parameters = $replication
  79. ->getSentinelConnection()
  80. ->getParameters();
  81. $this->assertSame($originalParameters, $parameters);
  82. $this->assertNotNull($parameters->password);
  83. $this->assertNotNull($parameters->database);
  84. }
  85. /**
  86. * @group disconnected
  87. */
  88. public function testMethodGetSentinelConnectionReturnsFirstAvailableSentinel()
  89. {
  90. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel&alias=sentinel1');
  91. $sentinel2 = $this->getMockSentinelConnection('tcp://127.0.0.1:5382?role=sentinel&alias=sentinel2');
  92. $sentinel3 = $this->getMockSentinelConnection('tcp://127.0.0.1:5383?role=sentinel&alias=sentinel3');
  93. $replication = $this->getReplicationConnection('svc', array($sentinel1, $sentinel2, $sentinel3));
  94. $this->assertSame($sentinel1, $replication->getSentinelConnection());
  95. }
  96. /**
  97. * @group disconnected
  98. */
  99. public function testMethodAddAttachesMasterOrSlaveNodesToReplication()
  100. {
  101. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  102. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  103. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  104. $slave2 = $this->getMockConnection('tcp://127.0.0.1:6383?role=slave');
  105. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  106. $replication->add($master);
  107. $replication->add($slave1);
  108. $replication->add($slave2);
  109. $this->assertSame($master, $replication->getConnectionById('127.0.0.1:6381'));
  110. $this->assertSame($slave1, $replication->getConnectionById('127.0.0.1:6382'));
  111. $this->assertSame($slave2, $replication->getConnectionById('127.0.0.1:6383'));
  112. $this->assertSame($master, $replication->getMaster());
  113. $this->assertSame(array($slave1, $slave2), $replication->getSlaves());
  114. }
  115. /**
  116. * @group disconnected
  117. * @FIXME
  118. */
  119. public function testMethodRemoveDismissesMasterOrSlaveNodesFromReplication()
  120. {
  121. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  122. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  123. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  124. $slave2 = $this->getMockConnection('tcp://127.0.0.1:6383?role=slave');
  125. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  126. $replication->add($master);
  127. $replication->add($slave1);
  128. $replication->add($slave2);
  129. $this->assertTrue($replication->remove($slave1));
  130. $this->assertTrue($replication->remove($sentinel1));
  131. $this->assertSame('127.0.0.1:6381', (string) $replication->getMaster());
  132. $this->assertCount(1, $slaves = $replication->getSlaves());
  133. $this->assertSame('127.0.0.1:6383', (string) $slaves[0]);
  134. }
  135. /**
  136. * @group disconnected
  137. */
  138. public function testMethodGetConnectionByIdOnEmptyReplication()
  139. {
  140. $replication = $this->getReplicationConnection('svc', array());
  141. $this->assertNull($replication->getConnectionById('127.0.0.1:6381'));
  142. }
  143. /**
  144. * @group disconnected
  145. */
  146. public function testMethodGetConnectionByRole()
  147. {
  148. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  149. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  150. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  151. $replication = $this->getReplicationConnection('svc', array());
  152. $replication->add($master);
  153. $replication->add($slave1);
  154. $replication->add($sentinel1);
  155. $this->assertSame($sentinel1, $replication->getConnectionByRole('sentinel'));
  156. $this->assertSame($master, $replication->getConnectionByRole('master'));
  157. $this->assertSame($slave1, $replication->getConnectionByRole('slave'));
  158. }
  159. /**
  160. * @group disconnected
  161. */
  162. public function testMethodGetConnectionByRoleOnEmptyReplicationForcesSentinelQueries()
  163. {
  164. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  165. $sentinel1
  166. ->expects($this->exactly(2))
  167. ->method('executeCommand')
  168. ->withConsecutive(
  169. $this->isRedisCommand('SENTINEL', array('get-master-addr-by-name', 'svc')),
  170. $this->isRedisCommand('SENTINEL', array('slaves', 'svc'))
  171. )
  172. ->will($this->onConsecutiveCalls(
  173. // SENTINEL get-master-addr-by-name svc
  174. array('127.0.0.1', '6381'),
  175. // SENTINEL slaves svc
  176. array(
  177. array(
  178. 'name', '127.0.0.1:6382',
  179. 'ip', '127.0.0.1',
  180. 'port', '6382',
  181. 'runid', '112cdebd22924a7d962be496f3a1c4c7c9bad93f',
  182. 'flags', 'slave',
  183. 'master-host', '127.0.0.1',
  184. 'master-port', '6381',
  185. ),
  186. )
  187. ));
  188. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  189. $this->assertSame($sentinel1, $replication->getConnectionByRole('sentinel'));
  190. $this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $replication->getConnectionByRole('master'));
  191. $this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $replication->getConnectionByRole('slave'));
  192. }
  193. /**
  194. * @group disconnected
  195. */
  196. public function testMethodGetConnectionByRoleUnknown()
  197. {
  198. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  199. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  200. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  201. $replication = $this->getReplicationConnection('svc', array());
  202. $replication->add($master);
  203. $replication->add($slave1);
  204. $replication->add($sentinel1);
  205. $this->assertNull($replication->getConnectionByRole('unknown'));
  206. }
  207. /**
  208. * @group disconnected
  209. */
  210. public function testMethodUpdateSentinelsFetchesSentinelNodes()
  211. {
  212. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  213. $sentinel1
  214. ->expects($this->once())
  215. ->method('executeCommand')
  216. ->with($this->isRedisCommand(
  217. 'SENTINEL', array('sentinels', 'svc')
  218. ))
  219. ->will($this->returnValue(
  220. array(
  221. array(
  222. 'name', '127.0.0.1:5382',
  223. 'ip', '127.0.0.1',
  224. 'port', '5382',
  225. 'runid', 'a113aa7a0d4870a85bb22b4b605fd26eb93ed40e',
  226. 'flags', 'sentinel',
  227. ),
  228. array(
  229. 'name', '127.0.0.1:5383',
  230. 'ip', '127.0.0.1',
  231. 'port', '5383',
  232. 'runid', 'f53b52d281be5cdd4873700c94846af8dbe47209',
  233. 'flags', 'sentinel',
  234. ),
  235. )
  236. ));
  237. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  238. $replication->updateSentinels();
  239. // TODO: sorry for the smell...
  240. $reflection = new \ReflectionProperty($replication, 'sentinels');
  241. $reflection->setAccessible(true);
  242. $expected = array(
  243. array('host' => '127.0.0.1', 'port' => '5381'),
  244. array('host' => '127.0.0.1', 'port' => '5382'),
  245. array('host' => '127.0.0.1', 'port' => '5383'),
  246. );
  247. $this->assertSame($sentinel1, $replication->getSentinelConnection());
  248. $this->assertSame($expected, array_intersect_key($expected, $reflection->getValue($replication)));
  249. }
  250. /**
  251. * @group disconnected
  252. */
  253. public function testMethodUpdateSentinelsRemovesCurrentSentinelAndRetriesNextOneOnFailure()
  254. {
  255. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel&alias=sentinel1');
  256. $sentinel1
  257. ->expects($this->once())
  258. ->method('executeCommand')
  259. ->with($this->isRedisCommand(
  260. 'SENTINEL', array('sentinels', 'svc')
  261. ))
  262. ->will($this->throwException(
  263. new Connection\ConnectionException($sentinel1, 'Unknown connection error [127.0.0.1:5381]')
  264. ));
  265. $sentinel2 = $this->getMockSentinelConnection('tcp://127.0.0.1:5382?role=sentinel&alias=sentinel2');
  266. $sentinel2
  267. ->expects($this->once())
  268. ->method('executeCommand')
  269. ->with($this->isRedisCommand(
  270. 'SENTINEL', array('sentinels', 'svc')
  271. ))
  272. ->will($this->returnValue(
  273. array(
  274. array(
  275. 'name', '127.0.0.1:5383',
  276. 'ip', '127.0.0.1',
  277. 'port', '5383',
  278. 'runid', 'f53b52d281be5cdd4873700c94846af8dbe47209',
  279. 'flags', 'sentinel',
  280. ),
  281. )
  282. ));
  283. $replication = $this->getReplicationConnection('svc', array($sentinel1, $sentinel2));
  284. $replication->updateSentinels();
  285. // TODO: sorry for the smell...
  286. $reflection = new \ReflectionProperty($replication, 'sentinels');
  287. $reflection->setAccessible(true);
  288. $expected = array(
  289. array('host' => '127.0.0.1', 'port' => '5382'),
  290. array('host' => '127.0.0.1', 'port' => '5383'),
  291. );
  292. $this->assertSame($sentinel2, $replication->getSentinelConnection());
  293. $this->assertSame($expected, array_intersect_key($expected, $reflection->getValue($replication)));
  294. }
  295. /**
  296. * @group disconnected
  297. * @expectedException Predis\ClientException
  298. * @expectedExceptionMessage No sentinel server available for autodiscovery.
  299. */
  300. public function testMethodUpdateSentinelsThrowsExceptionOnNoAvailableSentinel()
  301. {
  302. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  303. $sentinel1
  304. ->expects($this->once())
  305. ->method('executeCommand')
  306. ->with($this->isRedisCommand(
  307. 'SENTINEL', array('sentinels', 'svc')
  308. ))
  309. ->will($this->throwException(
  310. new Connection\ConnectionException($sentinel1, 'Unknown connection error [127.0.0.1:5381]')
  311. ));
  312. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  313. $replication->updateSentinels();
  314. }
  315. /**
  316. * @group disconnected
  317. */
  318. public function testMethodQuerySentinelFetchesMasterNodeSlaveNodesAndSentinelNodes()
  319. {
  320. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel&alias=sentinel1');
  321. $sentinel1
  322. ->expects($this->exactly(3))
  323. ->method('executeCommand')
  324. ->withConsecutive(
  325. $this->isRedisCommand('SENTINEL', array('sentinels', 'svc')),
  326. $this->isRedisCommand('SENTINEL', array('get-master-addr-by-name', 'svc')),
  327. $this->isRedisCommand('SENTINEL', array('slaves', 'svc'))
  328. )
  329. ->will($this->onConsecutiveCalls(
  330. // SENTINEL sentinels svc
  331. array(
  332. array(
  333. 'name', '127.0.0.1:5382',
  334. 'ip', '127.0.0.1',
  335. 'port', '5382',
  336. 'runid', 'a113aa7a0d4870a85bb22b4b605fd26eb93ed40e',
  337. 'flags', 'sentinel',
  338. ),
  339. ),
  340. // SENTINEL get-master-addr-by-name svc
  341. array('127.0.0.1', '6381'),
  342. // SENTINEL slaves svc
  343. array(
  344. array(
  345. 'name', '127.0.0.1:6382',
  346. 'ip', '127.0.0.1',
  347. 'port', '6382',
  348. 'runid', '112cdebd22924a7d962be496f3a1c4c7c9bad93f',
  349. 'flags', 'slave',
  350. 'master-host', '127.0.0.1',
  351. 'master-port', '6381',
  352. ),
  353. array(
  354. 'name', '127.0.0.1:6383',
  355. 'ip', '127.0.0.1',
  356. 'port', '6383',
  357. 'runid', '1c0bf1291797fbc5608c07a17da394147dc62817',
  358. 'flags', 'slave',
  359. 'master-host', '127.0.0.1',
  360. 'master-port', '6381',
  361. ),
  362. )
  363. ));
  364. $sentinel2 = $this->getMockSentinelConnection('tcp://127.0.0.1:5382?role=sentinel&alias=sentinel2');
  365. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  366. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  367. $slave2 = $this->getMockConnection('tcp://127.0.0.1:6383?role=slave');
  368. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  369. $replication->querySentinel();
  370. // TODO: sorry for the smell...
  371. $reflection = new \ReflectionProperty($replication, 'sentinels');
  372. $reflection->setAccessible(true);
  373. $sentinels = array(
  374. array('host' => '127.0.0.1', 'port' => '5381'),
  375. array('host' => '127.0.0.1', 'port' => '5382'),
  376. );
  377. $this->assertSame($sentinel1, $replication->getSentinelConnection());
  378. $this->assertSame($sentinels, array_intersect_key($sentinels, $reflection->getValue($replication)));
  379. $master = $replication->getMaster();
  380. $slaves = $replication->getSlaves();
  381. $this->assertSame('127.0.0.1:6381', (string) $master);
  382. $this->assertCount(2, $slaves);
  383. $this->assertSame('127.0.0.1:6382', (string) $slaves[0]);
  384. $this->assertSame('127.0.0.1:6383', (string) $slaves[1]);
  385. }
  386. /**
  387. * @group disconnected
  388. */
  389. public function testMethodGetMasterAsksSentinelForMasterOnMasterNotSet()
  390. {
  391. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  392. $sentinel1
  393. ->expects($this->at(0))
  394. ->method('executeCommand')
  395. ->with($this->isRedisCommand(
  396. 'SENTINEL', array('get-master-addr-by-name', 'svc')
  397. ))
  398. ->will($this->returnValue(
  399. array('127.0.0.1', '6381')
  400. ));
  401. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  402. $this->assertSame('127.0.0.1:6381', (string) $replication->getMaster());
  403. }
  404. /**
  405. * @group disconnected
  406. * @expectedException Predis\ClientException
  407. * @expectedExceptionMessage No sentinel server available for autodiscovery.
  408. */
  409. public function testMethodGetMasterThrowsExceptionOnNoAvailableSentinels()
  410. {
  411. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  412. $sentinel1
  413. ->expects($this->any())
  414. ->method('executeCommand')
  415. ->with($this->isRedisCommand(
  416. 'SENTINEL', array('get-master-addr-by-name', 'svc')
  417. ))
  418. ->will($this->throwException(
  419. new Connection\ConnectionException($sentinel1, 'Unknown connection error [127.0.0.1:5381]')
  420. ));
  421. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  422. $replication->getMaster();
  423. }
  424. /**
  425. * @group disconnected
  426. */
  427. public function testMethodGetSlavesOnEmptySlavePoolAsksSentinelForSlaves()
  428. {
  429. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  430. $sentinel1
  431. ->expects($this->at(0))
  432. ->method('executeCommand')
  433. ->with($this->isRedisCommand(
  434. 'SENTINEL', array('slaves', 'svc')
  435. ))
  436. ->will($this->returnValue(
  437. array(
  438. array(
  439. 'name', '127.0.0.1:6382',
  440. 'ip', '127.0.0.1',
  441. 'port', '6382',
  442. 'runid', '112cdebd22924a7d962be496f3a1c4c7c9bad93f',
  443. 'flags', 'slave',
  444. 'master-host', '127.0.0.1',
  445. 'master-port', '6381',
  446. ),
  447. array(
  448. 'name', '127.0.0.1:6383',
  449. 'ip', '127.0.0.1',
  450. 'port', '6383',
  451. 'runid', '1c0bf1291797fbc5608c07a17da394147dc62817',
  452. 'flags', 'slave',
  453. 'master-host', '127.0.0.1',
  454. 'master-port', '6381',
  455. ),
  456. )
  457. ));
  458. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  459. $slaves = $replication->getSlaves();
  460. $this->assertSame('127.0.0.1:6382', (string) $slaves[0]);
  461. $this->assertSame('127.0.0.1:6383', (string) $slaves[1]);
  462. }
  463. /**
  464. * @group disconnected
  465. * @expectedException Predis\ClientException
  466. * @expectedExceptionMessage No sentinel server available for autodiscovery.
  467. */
  468. public function testMethodGetSlavesThrowsExceptionOnNoAvailableSentinels()
  469. {
  470. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  471. $sentinel1
  472. ->expects($this->any())
  473. ->method('executeCommand')
  474. ->with($this->isRedisCommand(
  475. 'SENTINEL', array('slaves', 'svc')
  476. ))
  477. ->will($this->throwException(
  478. new Connection\ConnectionException($sentinel1, 'Unknown connection error [127.0.0.1:5381]')
  479. ));
  480. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  481. $replication->getSlaves();
  482. }
  483. /**
  484. * @group disconnected
  485. * @expectedException Predis\ClientException
  486. * @expectedExceptionMessage No sentinel server available for autodiscovery.
  487. */
  488. public function testMethodConnectThrowsExceptionOnConnectWithEmptySentinelsPool()
  489. {
  490. $replication = $this->getReplicationConnection('svc', array());
  491. $replication->connect();
  492. }
  493. /**
  494. * @group disconnected
  495. */
  496. public function testMethodConnectForcesConnectionToSlave()
  497. {
  498. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  499. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  500. $master
  501. ->expects($this->never())
  502. ->method('connect');
  503. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  504. $slave1
  505. ->expects($this->once())
  506. ->method('connect');
  507. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  508. $replication->add($master);
  509. $replication->add($slave1);
  510. $replication->connect();
  511. }
  512. /**
  513. * @group disconnected
  514. */
  515. public function testMethodConnectOnEmptySlavePoolAsksSentinelForSlavesAndForcesConnectionToSlave()
  516. {
  517. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  518. $sentinel1
  519. ->expects($this->any())
  520. ->method('executeCommand')
  521. ->with($this->isRedisCommand(
  522. 'SENTINEL', array('slaves', 'svc')
  523. ))
  524. ->will($this->returnValue(
  525. array(
  526. array(
  527. 'name', '127.0.0.1:6382',
  528. 'ip', '127.0.0.1',
  529. 'port', '6382',
  530. 'runid', '112cdebd22924a7d962be496f3a1c4c7c9bad93f',
  531. 'flags', 'slave',
  532. 'master-host', '127.0.0.1',
  533. 'master-port', '6381',
  534. ),
  535. )
  536. ));
  537. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  538. $master
  539. ->expects($this->never())
  540. ->method('connect');
  541. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  542. $slave1
  543. ->expects($this->once())
  544. ->method('connect');
  545. $factory = $this->getMock('Predis\Connection\FactoryInterface');
  546. $factory
  547. ->expects($this->once())
  548. ->method('create')
  549. ->with(array(
  550. 'host' => '127.0.0.1',
  551. 'port' => '6382',
  552. 'role' => 'slave',
  553. ))
  554. ->will($this->returnValue($slave1));
  555. $replication = $this->getReplicationConnection('svc', array($sentinel1), $factory);
  556. $replication->add($master);
  557. $replication->connect();
  558. }
  559. /**
  560. * @group disconnected
  561. */
  562. public function testMethodConnectOnEmptySlavePoolAsksSentinelForSlavesAndForcesConnectionToMasterIfStillEmpty()
  563. {
  564. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  565. $sentinel1
  566. ->expects($this->at(0))
  567. ->method('executeCommand')
  568. ->with($this->isRedisCommand(
  569. 'SENTINEL', array('slaves', 'svc')
  570. ))
  571. ->will($this->returnValue(
  572. array()
  573. ));
  574. $sentinel1
  575. ->expects($this->at(1))
  576. ->method('executeCommand')
  577. ->with($this->isRedisCommand(
  578. 'SENTINEL', array('get-master-addr-by-name', 'svc')
  579. ))
  580. ->will($this->returnValue(
  581. array('127.0.0.1', '6381')
  582. ));
  583. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  584. $master
  585. ->expects($this->once())
  586. ->method('connect');
  587. $factory = $this->getMock('Predis\Connection\FactoryInterface');
  588. $factory
  589. ->expects($this->once())
  590. ->method('create')
  591. ->with(array(
  592. 'host' => '127.0.0.1',
  593. 'port' => '6381',
  594. 'role' => 'master',
  595. ))
  596. ->will($this->returnValue($master));
  597. $replication = $this->getReplicationConnection('svc', array($sentinel1), $factory);
  598. $replication->connect();
  599. }
  600. /**
  601. * @group disconnected
  602. */
  603. public function testMethodDisconnectForcesDisconnectionOnAllConnectionsInPool()
  604. {
  605. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  606. $sentinel1
  607. ->expects($this->never())
  608. ->method('disconnect');
  609. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  610. $master
  611. ->expects($this->once())
  612. ->method('disconnect');
  613. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  614. $slave1
  615. ->expects($this->once())
  616. ->method('disconnect');
  617. $slave2 = $this->getMockConnection('tcp://127.0.0.1:6383?role=slave');
  618. $slave2
  619. ->expects($this->once())
  620. ->method('disconnect');
  621. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  622. $replication->add($master);
  623. $replication->add($slave1);
  624. $replication->add($slave2);
  625. $replication->disconnect();
  626. }
  627. /**
  628. * @group disconnected
  629. */
  630. public function testMethodIsConnectedReturnConnectionStatusOfCurrentConnection()
  631. {
  632. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  633. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  634. $slave1
  635. ->expects($this->exactly(2))
  636. ->method('isConnected')
  637. ->will($this->onConsecutiveCalls(true, false));
  638. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  639. $replication->add($slave1);
  640. $this->assertFalse($replication->isConnected());
  641. $replication->connect();
  642. $this->assertTrue($replication->isConnected());
  643. $replication->getConnectionById('127.0.0.1:6382')->disconnect();
  644. $this->assertFalse($replication->isConnected());
  645. }
  646. /**
  647. * @group disconnected
  648. */
  649. public function testMethodGetConnectionByIdReturnsConnectionWhenFound()
  650. {
  651. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  652. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  653. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  654. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  655. $replication->add($master);
  656. $replication->add($slave1);
  657. $this->assertSame($master, $replication->getConnectionById('127.0.0.1:6381'));
  658. $this->assertSame($slave1, $replication->getConnectionById('127.0.0.1:6382'));
  659. $this->assertNull($replication->getConnectionById('127.0.0.1:6383'));
  660. }
  661. /**
  662. * @group disconnected
  663. */
  664. public function testMethodSwitchToSelectsCurrentConnection()
  665. {
  666. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  667. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  668. $master
  669. ->expects($this->once())
  670. ->method('connect');
  671. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  672. $slave1
  673. ->expects($this->never())
  674. ->method('connect');
  675. $slave2 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  676. $slave2
  677. ->expects($this->once())
  678. ->method('connect');
  679. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  680. $replication->add($master);
  681. $replication->add($slave1);
  682. $replication->add($slave2);
  683. $replication->switchTo($master);
  684. $this->assertSame($master, $replication->getCurrent());
  685. $replication->switchTo($slave2);
  686. $this->assertSame($slave2, $replication->getCurrent());
  687. }
  688. /**
  689. * @group disconnected
  690. * @expectedException InvalidArgumentException
  691. * @expectedExceptionMessage Invalid connection or connection not found.
  692. */
  693. public function testMethodSwitchToThrowsExceptionOnConnectionNotFound()
  694. {
  695. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  696. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  697. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  698. $slave2 = $this->getMockConnection('tcp://127.0.0.1:6383?role=slave');
  699. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  700. $replication->add($master);
  701. $replication->add($slave1);
  702. $replication->switchTo($slave2);
  703. }
  704. /**
  705. * @group disconnected
  706. */
  707. public function testMethodSwitchToMasterSelectsCurrentConnectionToMaster()
  708. {
  709. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  710. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  711. $master
  712. ->expects($this->once())
  713. ->method('connect');
  714. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  715. $slave1
  716. ->expects($this->never())
  717. ->method('connect');
  718. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  719. $replication->add($master);
  720. $replication->add($slave1);
  721. $replication->switchToMaster();
  722. $this->assertSame($master, $replication->getCurrent());
  723. }
  724. /**
  725. * @group disconnected
  726. */
  727. public function testMethodSwitchToSlaveSelectsCurrentConnectionToRandomSlave()
  728. {
  729. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  730. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  731. $master
  732. ->expects($this->never())
  733. ->method('connect');
  734. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  735. $slave1
  736. ->expects($this->once())
  737. ->method('connect');
  738. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  739. $replication->add($master);
  740. $replication->add($slave1);
  741. $replication->switchToSlave();
  742. $this->assertSame($slave1, $replication->getCurrent());
  743. }
  744. /**
  745. * @group disconnected
  746. */
  747. public function testGetConnectionByCommandReturnsMasterForWriteCommands()
  748. {
  749. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  750. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  751. $master
  752. ->expects($this->exactly(2))
  753. ->method('isConnected')
  754. ->will($this->onConsecutiveCalls(false, true));
  755. $master
  756. ->expects($this->at(3))
  757. ->method('executeCommand')
  758. ->with($this->isRedisCommand('ROLE'))
  759. ->will($this->returnValue(array(
  760. 'master', 3129659, array(array('127.0.0.1', 6382, 3129242)),
  761. )));
  762. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  763. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  764. $replication->add($master);
  765. $replication->add($slave1);
  766. $this->assertSame($master, $replication->getConnectionByCommand(
  767. Command\RawCommand::create('set', 'key', 'value')
  768. ));
  769. $this->assertSame($master, $replication->getConnectionByCommand(
  770. Command\RawCommand::create('del', 'key')
  771. ));
  772. }
  773. /**
  774. * @group disconnected
  775. */
  776. public function testGetConnectionByCommandReturnsSlaveForReadOnlyCommands()
  777. {
  778. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  779. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  780. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  781. $slave1
  782. ->expects($this->exactly(2))
  783. ->method('isConnected')
  784. ->will($this->onConsecutiveCalls(false, true));
  785. $slave1
  786. ->expects($this->at(3))
  787. ->method('executeCommand')
  788. ->with($this->isRedisCommand('ROLE'))
  789. ->will($this->returnValue(array(
  790. 'slave', '127.0.0.1', 9000, 'connected', 3167038,
  791. )));
  792. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  793. $replication->add($master);
  794. $replication->add($slave1);
  795. $this->assertSame($slave1, $replication->getConnectionByCommand(
  796. Command\RawCommand::create('get', 'key')
  797. ));
  798. $this->assertSame($slave1, $replication->getConnectionByCommand(
  799. Command\RawCommand::create('exists', 'key')
  800. ));
  801. }
  802. /**
  803. * @group disconnected
  804. */
  805. public function testGetConnectionByCommandSwitchesToMasterAfterWriteCommand()
  806. {
  807. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  808. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  809. $master
  810. ->expects($this->exactly(2))
  811. ->method('isConnected')
  812. ->will($this->onConsecutiveCalls(false, true));
  813. $master
  814. ->expects($this->at(3))
  815. ->method('executeCommand')
  816. ->with($this->isRedisCommand('ROLE'))
  817. ->will($this->returnValue(array(
  818. 'master', 3129659, array(array('127.0.0.1', 6382, 3129242)),
  819. )));
  820. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  821. $slave1
  822. ->expects($this->exactly(1))
  823. ->method('isConnected')
  824. ->will($this->onConsecutiveCalls(false));
  825. $slave1
  826. ->expects($this->at(3))
  827. ->method('executeCommand')
  828. ->with($this->isRedisCommand('ROLE'))
  829. ->will($this->returnValue(array(
  830. 'slave', '127.0.0.1', 9000, 'connected', 3167038,
  831. )));
  832. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  833. $replication->add($master);
  834. $replication->add($slave1);
  835. $this->assertSame($slave1, $replication->getConnectionByCommand(
  836. Command\RawCommand::create('exists', 'key')
  837. ));
  838. $this->assertSame($master, $replication->getConnectionByCommand(
  839. Command\RawCommand::create('set', 'key', 'value')
  840. ));
  841. $this->assertSame($master, $replication->getConnectionByCommand(
  842. Command\RawCommand::create('get', 'key')
  843. ));
  844. }
  845. /**
  846. * @group disconnected
  847. * @expectedException Predis\Replication\RoleException
  848. * @expectedExceptionMessage Expected master but got slave [127.0.0.1:6381]
  849. */
  850. public function testGetConnectionByCommandThrowsExceptionOnNodeRoleMismatch()
  851. {
  852. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  853. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  854. $master
  855. ->expects($this->once())
  856. ->method('isConnected')
  857. ->will($this->returnValue(false));
  858. $master
  859. ->expects($this->at(3))
  860. ->method('executeCommand')
  861. ->with($this->isRedisCommand('ROLE'))
  862. ->will($this->returnValue(array(
  863. 'slave', '127.0.0.1', 9000, 'connected', 3167038,
  864. )));
  865. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  866. $replication->add($master);
  867. $replication->getConnectionByCommand(Command\RawCommand::create('del', 'key'));
  868. }
  869. /**
  870. * @group disconnected
  871. */
  872. public function testGetConnectionByCommandReturnsMasterForReadOnlyOperationsOnUnavailableSlaves()
  873. {
  874. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  875. $sentinel1
  876. ->expects($this->once())
  877. ->method('executeCommand')
  878. ->with($this->isRedisCommand(
  879. 'SENTINEL', array('slaves', 'svc')
  880. ))
  881. ->will($this->returnValue(
  882. array(
  883. array(
  884. 'name', '127.0.0.1:6382',
  885. 'ip', '127.0.0.1',
  886. 'port', '6382',
  887. 'runid', '1c0bf1291797fbc5608c07a17da394147dc62817',
  888. 'flags', 'slave,s_down,disconnected',
  889. 'master-host', '127.0.0.1',
  890. 'master-port', '6381',
  891. ),
  892. )
  893. ));
  894. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  895. $master
  896. ->expects($this->once())
  897. ->method('isConnected')
  898. ->will($this->returnValue(false));
  899. $master
  900. ->expects($this->at(3))
  901. ->method('executeCommand')
  902. ->with($this->isRedisCommand('ROLE'))
  903. ->will($this->returnValue(array(
  904. 'master', '0', array(),
  905. )));
  906. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  907. $replication->add($master);
  908. $replication->getConnectionByCommand(Command\RawCommand::create('get', 'key'));
  909. }
  910. /**
  911. * @group disconnected
  912. */
  913. public function testMethodExecuteCommandSendsCommandToNodeAndReturnsResponse()
  914. {
  915. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  916. $cmdGet = Command\RawCommand::create('get', 'key');
  917. $cmdGetResponse = 'value';
  918. $cmdSet = Command\RawCommand::create('set', 'key', 'value');
  919. $cmdSetResponse = Response\Status::get('OK');
  920. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  921. $master
  922. ->expects($this->any())
  923. ->method('isConnected')
  924. ->will($this->returnValue(true));
  925. $master
  926. ->expects($this->at(3))
  927. ->method('executeCommand')
  928. ->with($this->isRedisCommand(
  929. 'SET', array('key', $cmdGetResponse)
  930. ))
  931. ->will($this->returnValue($cmdSetResponse));
  932. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  933. $slave1
  934. ->expects($this->any())
  935. ->method('isConnected')
  936. ->will($this->returnValue(true));
  937. $slave1
  938. ->expects($this->at(3))
  939. ->method('executeCommand')
  940. ->with($this->isRedisCommand(
  941. 'GET', array('key')
  942. ))
  943. ->will($this->returnValue($cmdGetResponse));
  944. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  945. $replication->add($master);
  946. $replication->add($slave1);
  947. $this->assertSame($cmdGetResponse, $replication->executeCommand($cmdGet));
  948. $this->assertSame($cmdSetResponse, $replication->executeCommand($cmdSet));
  949. }
  950. /**
  951. * @group disconnected
  952. */
  953. public function testMethodExecuteCommandRetriesReadOnlyCommandOnNextSlaveOnFailure()
  954. {
  955. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  956. $sentinel1
  957. ->expects($this->any())
  958. ->method('executeCommand')
  959. ->with($this->isRedisCommand(
  960. 'SENTINEL', array('slaves', 'svc')
  961. ))
  962. ->will($this->returnValue(
  963. array(
  964. array(
  965. 'name', '127.0.0.1:6383',
  966. 'ip', '127.0.0.1',
  967. 'port', '6383',
  968. 'runid', '1c0bf1291797fbc5608c07a17da394147dc62817',
  969. 'flags', 'slave',
  970. 'master-host', '127.0.0.1',
  971. 'master-port', '6381',
  972. ),
  973. )
  974. ));
  975. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  976. $master
  977. ->expects($this->any())
  978. ->method('isConnected')
  979. ->will($this->returnValue(true));
  980. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  981. $slave1
  982. ->expects($this->any())
  983. ->method('isConnected')
  984. ->will($this->returnValue(true));
  985. $slave1
  986. ->expects($this->at(3))
  987. ->method('executeCommand')
  988. ->with($this->isRedisCommand(
  989. 'GET', array('key')
  990. ))
  991. ->will($this->throwException(
  992. new Connection\ConnectionException($slave1, 'Unknown connection error [127.0.0.1:6382]')
  993. ));
  994. $slave2 = $this->getMockConnection('tcp://127.0.0.1:6383?role=slave');
  995. $slave2
  996. ->expects($this->any())
  997. ->method('isConnected')
  998. ->will($this->returnValue(true));
  999. $slave2
  1000. ->expects($this->at(3))
  1001. ->method('executeCommand')
  1002. ->with($this->isRedisCommand(
  1003. 'GET', array('key')
  1004. ))
  1005. ->will($this->returnValue('value'));
  1006. $factory = $this->getMock('Predis\Connection\FactoryInterface');
  1007. $factory
  1008. ->expects($this->once())
  1009. ->method('create')
  1010. ->with(array(
  1011. 'host' => '127.0.0.1',
  1012. 'port' => '6383',
  1013. 'role' => 'slave',
  1014. ))
  1015. ->will($this->returnValue($slave2));
  1016. $replication = $this->getReplicationConnection('svc', array($sentinel1), $factory);
  1017. $replication->add($master);
  1018. $replication->add($slave1);
  1019. $this->assertSame('value', $replication->executeCommand(
  1020. Command\RawCommand::create('get', 'key')
  1021. ));
  1022. }
  1023. /**
  1024. * @group disconnected
  1025. */
  1026. public function testMethodExecuteCommandRetriesWriteCommandOnNewMasterOnFailure()
  1027. {
  1028. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  1029. $sentinel1
  1030. ->expects($this->any())
  1031. ->method('executeCommand')
  1032. ->with($this->isRedisCommand(
  1033. 'SENTINEL', array('get-master-addr-by-name', 'svc')
  1034. ))
  1035. ->will($this->returnValue(
  1036. array('127.0.0.1', '6391')
  1037. ));
  1038. $masterOld = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  1039. $masterOld
  1040. ->expects($this->any())
  1041. ->method('isConnected')
  1042. ->will($this->returnValue(true));
  1043. $masterOld
  1044. ->expects($this->at(3))
  1045. ->method('executeCommand')
  1046. ->with($this->isRedisCommand(
  1047. 'DEL', array('key')
  1048. ))
  1049. ->will($this->throwException(
  1050. new Connection\ConnectionException($masterOld, 'Unknown connection error [127.0.0.1:6381]')
  1051. ));
  1052. $masterNew = $this->getMockConnection('tcp://127.0.0.1:6391?role=master');
  1053. $masterNew
  1054. ->expects($this->any())
  1055. ->method('isConnected')
  1056. ->will($this->returnValue(true));
  1057. $masterNew
  1058. ->expects($this->at(3))
  1059. ->method('executeCommand')
  1060. ->with($this->isRedisCommand(
  1061. 'DEL', array('key')
  1062. ))
  1063. ->will($this->returnValue(1));
  1064. $factory = $this->getMock('Predis\Connection\FactoryInterface');
  1065. $factory
  1066. ->expects($this->once())
  1067. ->method('create')
  1068. ->with(array(
  1069. 'host' => '127.0.0.1',
  1070. 'port' => '6391',
  1071. 'role' => 'master',
  1072. ))
  1073. ->will($this->returnValue($masterNew));
  1074. $replication = $this->getReplicationConnection('svc', array($sentinel1), $factory);
  1075. $replication->add($masterOld);
  1076. $this->assertSame(1, $replication->executeCommand(
  1077. Command\RawCommand::create('del', 'key')
  1078. ));
  1079. }
  1080. /**
  1081. * @group disconnected
  1082. * @expectedException Predis\Response\ServerException
  1083. * @expectedExceptionMessage ERR No such master with that name
  1084. */
  1085. public function testMethodExecuteCommandThrowsExceptionOnUnknownServiceName()
  1086. {
  1087. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  1088. $sentinel1
  1089. ->expects($this->any())
  1090. ->method('executeCommand')
  1091. ->with($this->isRedisCommand(
  1092. 'SENTINEL', array('get-master-addr-by-name', 'svc')
  1093. ))
  1094. ->will($this->returnValue(null));
  1095. $masterOld = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  1096. $masterOld
  1097. ->expects($this->any())
  1098. ->method('isConnected')
  1099. ->will($this->returnValue(true));
  1100. $masterOld
  1101. ->expects($this->at(3))
  1102. ->method('executeCommand')
  1103. ->with($this->isRedisCommand(
  1104. 'DEL', array('key')
  1105. ))
  1106. ->will($this->throwException(
  1107. new Connection\ConnectionException($masterOld, 'Unknown connection error [127.0.0.1:6381]')
  1108. ));
  1109. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  1110. $replication->add($masterOld);
  1111. $replication->executeCommand(
  1112. Command\RawCommand::create('del', 'key')
  1113. );
  1114. }
  1115. /**
  1116. * @group disconnected
  1117. * @expectedException Predis\ClientException
  1118. * @expectedExceptionMessage No sentinel server available for autodiscovery.
  1119. */
  1120. public function testMethodExecuteCommandThrowsExceptionOnConnectionFailureAndNoAvailableSentinels()
  1121. {
  1122. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  1123. $sentinel1
  1124. ->expects($this->any())
  1125. ->method('executeCommand')
  1126. ->with($this->isRedisCommand(
  1127. 'SENTINEL', array('get-master-addr-by-name', 'svc')
  1128. ))
  1129. ->will($this->throwException(
  1130. new Connection\ConnectionException($sentinel1, 'Unknown connection error [127.0.0.1:5381]')
  1131. ));
  1132. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  1133. $master
  1134. ->expects($this->any())
  1135. ->method('isConnected')
  1136. ->will($this->returnValue(true));
  1137. $master
  1138. ->expects($this->at(3))
  1139. ->method('executeCommand')
  1140. ->with($this->isRedisCommand(
  1141. 'DEL', array('key')
  1142. ))
  1143. ->will($this->throwException(
  1144. new Connection\ConnectionException($master, 'Unknown connection error [127.0.0.1:6381]')
  1145. ));
  1146. $replication = $this->getReplicationConnection('svc', array($sentinel1));
  1147. $replication->add($master);
  1148. $replication->executeCommand(
  1149. Command\RawCommand::create('del', 'key')
  1150. );
  1151. }
  1152. /**
  1153. * @group disconnected
  1154. */
  1155. public function testMethodGetReplicationStrategyReturnsInstance()
  1156. {
  1157. $strategy = new Replication\ReplicationStrategy();
  1158. $factory = new Connection\Factory();
  1159. $replication = new SentinelReplication(
  1160. 'svc', array('tcp://127.0.0.1:5381?role=sentinel'), $factory, $strategy
  1161. );
  1162. $this->assertSame($strategy, $replication->getReplicationStrategy());
  1163. }
  1164. /**
  1165. * @group disconnected
  1166. */
  1167. public function testMethodSerializeCanSerializeWholeObject()
  1168. {
  1169. $sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
  1170. $master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
  1171. $slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
  1172. $slave2 = $this->getMockConnection('tcp://127.0.0.1:6383?role=slave');
  1173. $strategy = new Replication\ReplicationStrategy();
  1174. $factory = new Connection\Factory();
  1175. $replication = new SentinelReplication('svc', array($sentinel1), $factory, $strategy);
  1176. $replication->add($master);
  1177. $replication->add($slave1);
  1178. $replication->add($slave2);
  1179. $unserialized = unserialize(serialize($replication));
  1180. $this->assertEquals($master, $unserialized->getConnectionById('127.0.0.1:6381'));
  1181. $this->assertEquals($slave1, $unserialized->getConnectionById('127.0.0.1:6382'));
  1182. $this->assertEquals($master, $unserialized->getConnectionById('127.0.0.1:6383'));
  1183. $this->assertEquals($strategy, $unserialized->getReplicationStrategy());
  1184. }
  1185. // ******************************************************************** //
  1186. // ---- HELPER METHODS ------------------------------------------------ //
  1187. // ******************************************************************** //
  1188. /**
  1189. * Creates a new instance of replication connection.
  1190. *
  1191. * @param string $service Name of the service
  1192. * @param array $sentinels Array of sentinels
  1193. * @param ConnectionFactoryInterface|null $factory Optional connection factory instance.
  1194. *
  1195. * @return SentinelReplication
  1196. */
  1197. protected function getReplicationConnection($service, $sentinels, Connection\FactoryInterface $factory = null)
  1198. {
  1199. $factory = $factory ?: new Connection\Factory();
  1200. $replication = new SentinelReplication($service, $sentinels, $factory);
  1201. $replication->setRetryWait(0);
  1202. return $replication;
  1203. }
  1204. /**
  1205. * Returns a base mocked connection from Predis\Connection\NodeConnectionInterface.
  1206. *
  1207. * @param mixed $parameters Optional parameters.
  1208. *
  1209. * @return mixed
  1210. */
  1211. protected function getMockSentinelConnection($parameters = null)
  1212. {
  1213. $connection = $this->getMockConnection($parameters);
  1214. return $connection;
  1215. }
  1216. }