From ed2ddeede5df61f1ae80e18eb9f2d3e7b95c031d Mon Sep 17 00:00:00 2001 From: Midhun Monachan <70493664+midhunmonachan@users.noreply.github.com> Date: Wed, 30 Sep 2026 03:57:38 -0400 Subject: [PATCH] Fix client iteration with master/slave and Sentinel replication (#1730) * Fix client iteration with master/slave replication * Fix client iteration with Sentinel replication --------- Co-authored-by: Vladyslav Vildanov <117659936+vladvildanov@users.noreply.github.com> --- CHANGELOG.md | 1 + .../Replication/MasterSlaveReplication.php | 8 ++++ .../Replication/SentinelReplication.php | 8 ++++ .../MasterSlaveReplicationTest.php | 20 +++++++++ .../Replication/SentinelReplicationTest.php | 42 +++++++++++++++++++ 5 files changed, 79 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37d2fa7b..d62a1bfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Added optional `CELL_SIZE` argument for `CMS.INITBYDIM` and `CMS.INITBYPROB` ### Changed ### Fixed +- Fixed client iteration with master/slave and Sentinel replication (#923) ## v3.6.1 (2026-09-17) ### Fixed diff --git a/src/Connection/Replication/MasterSlaveReplication.php b/src/Connection/Replication/MasterSlaveReplication.php index b7abf7de..d23805be 100644 --- a/src/Connection/Replication/MasterSlaveReplication.php +++ b/src/Connection/Replication/MasterSlaveReplication.php @@ -588,6 +588,14 @@ class MasterSlaveReplication extends AbstractAggregateConnection implements Repl return null; } + /** + * @return string + */ + public function __toString() + { + return implode(',', array_keys($this->pool)); + } + /** * Handle connection exception. * diff --git a/src/Connection/Replication/SentinelReplication.php b/src/Connection/Replication/SentinelReplication.php index af4376f6..0ae7af6e 100644 --- a/src/Connection/Replication/SentinelReplication.php +++ b/src/Connection/Replication/SentinelReplication.php @@ -844,4 +844,12 @@ class SentinelReplication extends AbstractAggregateConnection implements Replica return null; } + + /** + * @return string + */ + public function __toString() + { + return implode(',', array_keys($this->pool)); + } } diff --git a/tests/Predis/Connection/Replication/MasterSlaveReplicationTest.php b/tests/Predis/Connection/Replication/MasterSlaveReplicationTest.php index 3386eef0..8b617dcd 100644 --- a/tests/Predis/Connection/Replication/MasterSlaveReplicationTest.php +++ b/tests/Predis/Connection/Replication/MasterSlaveReplicationTest.php @@ -13,6 +13,7 @@ namespace Predis\Connection\Replication; use PHPUnit\Framework\MockObject\MockObject; +use Predis\Client; use Predis\Command; use Predis\Connection; use Predis\Connection\Parameters; @@ -28,6 +29,25 @@ use RuntimeException; class MasterSlaveReplicationTest extends PredisTestCase { + /** + * @group disconnected + */ + public function testClientIterationKeepsReplicationConnection(): void + { + $client = new Client([ + 'tcp://127.0.0.1:6379?role=master', + 'tcp://127.0.0.1:6380?role=slave', + ], ['replication' => 'predis']); + + $iterator = $client->getIterator(); + + $this->assertCount(1, $iterator); + $this->assertSame('127.0.0.1:6379,127.0.0.1:6380', $iterator->key()); + $this->assertSame($client->getConnection(), $iterator->current()->getConnection()); + $this->assertSame($client->getOptions(), $iterator->current()->getOptions()); + $this->assertFalse($client->isConnected()); + } + /** * @group disconnected */ diff --git a/tests/Predis/Connection/Replication/SentinelReplicationTest.php b/tests/Predis/Connection/Replication/SentinelReplicationTest.php index 46766580..159b2ae2 100644 --- a/tests/Predis/Connection/Replication/SentinelReplicationTest.php +++ b/tests/Predis/Connection/Replication/SentinelReplicationTest.php @@ -13,6 +13,7 @@ namespace Predis\Connection\Replication; use PHPUnit\Framework\MockObject\MockObject; +use Predis\Client; use Predis\Command; use Predis\Connection; use Predis\Connection\Parameters; @@ -31,6 +32,47 @@ use RuntimeException; class SentinelReplicationTest extends PredisTestCase { + /** + * @group disconnected + */ + public function testClientIterationKeepsReplicationConnectionBeforeDiscovery(): void + { + $factory = $this->getMockBuilder(Connection\FactoryInterface::class)->getMock(); + $factory->expects($this->never())->method('create'); + + $client = new Client([ + 'tcp://127.0.0.1:26379', + 'tcp://127.0.0.1:26380', + ], ['replication' => 'sentinel', 'service' => 'svc', 'connections' => $factory]); + + $iterator = $client->getIterator(); + + $this->assertCount(1, $iterator); + $this->assertSame('', $iterator->key()); + $this->assertSame($client->getConnection(), $iterator->current()->getConnection()); + $this->assertSame($client->getOptions(), $iterator->current()->getOptions()); + $this->assertFalse($client->isConnected()); + } + + /** + * @group disconnected + */ + public function testClientIterationKeepsReplicationConnectionWithKnownNodes(): void + { + $replication = $this->getReplicationConnection('svc', []); + $replication->add(new StreamConnection(Parameters::create('tcp://127.0.0.1:6379?role=master'))); + $replication->add(new StreamConnection(Parameters::create('tcp://127.0.0.1:6380?role=slave'))); + + $client = new Client($replication); + $iterator = $client->getIterator(); + + $this->assertCount(1, $iterator); + $this->assertSame('127.0.0.1:6379,127.0.0.1:6380', $iterator->key()); + $this->assertSame($replication, $iterator->current()->getConnection()); + $this->assertSame($client->getOptions(), $iterator->current()->getOptions()); + $this->assertFalse($client->isConnected()); + } + /** * @group disconnected */