From 09e88b6fd02161a1cc3b150c6cb668a2f78e17ab Mon Sep 17 00:00:00 2001 From: Vladyslav Vildanov <117659936+vladvildanov@users.noreply.github.com> Date: Mon, 24 Feb 2025 09:51:55 +0200 Subject: [PATCH] fix: Bug with single persistent connection to the same resource (#1512) * fix: Bug with single persistent connection to the same resource * Fixed tests * Additional test fix * Codestyle fixes --- src/Connection/Parameters.php | 5 +++ src/Connection/ParametersInterface.php | 1 + src/Connection/Resource/StreamFactory.php | 12 ++++++ src/Connection/StreamConnection.php | 1 + tests/Predis/ClientTest.php | 38 +++++++++++++++++++ .../Connection/StreamConnectionTest.php | 16 ++++---- 6 files changed, 65 insertions(+), 8 deletions(-) diff --git a/src/Connection/Parameters.php b/src/Connection/Parameters.php index c03fc543..757d6bb0 100644 --- a/src/Connection/Parameters.php +++ b/src/Connection/Parameters.php @@ -166,6 +166,11 @@ class Parameters implements ParametersInterface } } + public function __set($parameter, $value) + { + $this->parameters[$parameter] = $value; + } + /** * {@inheritdoc} */ diff --git a/src/Connection/ParametersInterface.php b/src/Connection/ParametersInterface.php index 84295138..91f30cd0 100644 --- a/src/Connection/ParametersInterface.php +++ b/src/Connection/ParametersInterface.php @@ -28,6 +28,7 @@ namespace Predis\Connection; * @property float $timeout Timeout for the connect() operation. * @property float $read_write_timeout Timeout for read() and write() operations. * @property bool $persistent Leaves the connection open after a GC collection. + * @property string $conn_uid Unique identifier of connection, needs to create a multiple persistent connections to the same resource. * @property string $username Username to access Redis (see the AUTH command). * @property string $password Password to access Redis (see the AUTH command). * @property string $database Database index (see the SELECT command). diff --git a/src/Connection/Resource/StreamFactory.php b/src/Connection/Resource/StreamFactory.php index c3fb2335..45b850bf 100644 --- a/src/Connection/Resource/StreamFactory.php +++ b/src/Connection/Resource/StreamFactory.php @@ -194,6 +194,18 @@ class StreamFactory implements StreamFactoryInterface $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0); $context = stream_context_create(['socket' => ['tcp_nodelay' => (bool) $parameters->tcp_nodelay]]); + if ( + (isset($parameters->persistent) && $parameters->persistent) + && (isset($parameters->conn_uid) && $parameters->conn_uid) + ) { + $conn_uid = '/' . $parameters->conn_uid; + } else { + $conn_uid = ''; + } + + // Needs to create multiple persistent connections to the same resource + $address = $address . $conn_uid; + if (!$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags, $context)) { $this->onInitializationError($resource, $parameters, trim($errstr), $errno); } diff --git a/src/Connection/StreamConnection.php b/src/Connection/StreamConnection.php index d999671e..b849703e 100644 --- a/src/Connection/StreamConnection.php +++ b/src/Connection/StreamConnection.php @@ -59,6 +59,7 @@ class StreamConnection extends AbstractConnection public function __construct(ParametersInterface $parameters, ?StreamFactoryInterface $factory = null) { parent::__construct($parameters); + $this->parameters->conn_uid = spl_object_hash($this); $this->streamFactory = $factory ?? new StreamFactory(); } diff --git a/tests/Predis/ClientTest.php b/tests/Predis/ClientTest.php index 1e85053c..331a31ff 100644 --- a/tests/Predis/ClientTest.php +++ b/tests/Predis/ClientTest.php @@ -1323,6 +1323,44 @@ class ClientTest extends PredisTestCase $this->assertSame(Client::VERSION, $libVer); } + /** + * @group connected + */ + public function testClientsCreateDifferentPersistentConnections(): void + { + $client1 = new Client($this->getParameters(['database' => 14, 'persistent' => true])); + $client2 = new Client($this->getParameters(['database' => 15, 'persistent' => true])); + + $client1->set('foo', 'bar'); + $client2->set('foo', 'baz'); + + $this->assertSame('bar', $client1->get('foo')); + $this->assertSame('baz', $client2->get('foo')); + } + + /** + * @group connected + * @group cluster + * @requiresRedisVersion >= 2.0.0 + */ + public function testClusterClientsCreateDifferentPersistentConnections(): void + { + $client1 = new Client( + $this->getDefaultParametersArray(), + ['cluster' => 'redis', 'parameters' => ['persistent' => true]] + ); + $client2 = new Client( + $this->getDefaultParametersArray(), + ['cluster' => 'redis', 'parameters' => ['persistent' => true]] + ); + + $client1->set('{shard1}foo', 'bar'); + $client2->set('{shard2}foo', 'baz'); + + $this->assertSame('bar', $client1->get('{shard1}foo')); + $this->assertSame('baz', $client2->get('{shard2}foo')); + } + /** * @group connected * @group cluster diff --git a/tests/Predis/Connection/StreamConnectionTest.php b/tests/Predis/Connection/StreamConnectionTest.php index 464e6a8e..d1fa48f8 100644 --- a/tests/Predis/Connection/StreamConnectionTest.php +++ b/tests/Predis/Connection/StreamConnectionTest.php @@ -94,7 +94,7 @@ class StreamConnectionTest extends PredisConnectionTestCase $this->mockStreamFactory ->expects($this->once()) ->method('createStream') - ->with(new Parameters()) + ->withAnyParameters() ->willReturn($this->mockStream); $this->mockStream @@ -125,7 +125,7 @@ class StreamConnectionTest extends PredisConnectionTestCase $this->mockStreamFactory ->expects($this->once()) ->method('createStream') - ->with(new Parameters()) + ->withAnyParameters() ->willReturn($this->mockStream); $this->mockStream @@ -190,7 +190,7 @@ class StreamConnectionTest extends PredisConnectionTestCase $this->mockStreamFactory ->expects($this->once()) ->method('createStream') - ->with(new Parameters()) + ->withAnyParameters() ->willReturn($this->mockStream); $this->mockStream @@ -215,7 +215,7 @@ class StreamConnectionTest extends PredisConnectionTestCase $this->mockStreamFactory ->expects($this->once()) ->method('createStream') - ->with(new Parameters()) + ->withAnyParameters() ->willReturn($this->mockStream); $this->mockStream @@ -241,7 +241,7 @@ class StreamConnectionTest extends PredisConnectionTestCase $this->mockStreamFactory ->expects($this->once()) ->method('createStream') - ->with(new Parameters()) + ->withAnyParameters() ->willReturn($this->mockStream); $this->mockStream @@ -265,7 +265,7 @@ class StreamConnectionTest extends PredisConnectionTestCase $this->mockStreamFactory ->expects($this->once()) ->method('createStream') - ->with(new Parameters()) + ->withAnyParameters() ->willReturn($this->mockStream); $this->mockStream @@ -480,7 +480,7 @@ class StreamConnectionTest extends PredisConnectionTestCase $this->mockStreamFactory ->expects($this->once()) ->method('createStream') - ->with(new Parameters()) + ->withAnyParameters() ->willReturn($this->mockStream); $this->mockStream @@ -503,7 +503,7 @@ class StreamConnectionTest extends PredisConnectionTestCase $this->mockStreamFactory ->expects($this->once()) ->method('createStream') - ->with(new Parameters()) + ->withAnyParameters() ->willReturn($this->mockStream); $this->mockStream