From 869eb626d40bd3e89c1ee0256be114a23490139b Mon Sep 17 00:00:00 2001 From: upstream Date: Thu, 8 Oct 2026 09:41:25 +0200 Subject: [PATCH] Upstream predis/predis state of the touched files before 00611e08e0 --- CHANGELOG.md | 2 - src/Pipeline/ConnectionErrorProof.php | 10 +- .../Pipeline/ConnectionErrorProofTest.php | 216 ------------------ 3 files changed, 5 insertions(+), 223 deletions(-) delete mode 100644 tests/Predis/Pipeline/ConnectionErrorProofTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 73ba526..30e2e1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,6 @@ - Fixed cluster CAS transactions returning transaction objects for reads and sending `MULTI` twice - Fixed `MultiExec::multi()` being ignored as the first call of a CAS transaction - Fixed cleanup of empty transactions and transaction callbacks that throw PHP errors -- Fixed `spl_object_hash()` deprecation on PHP 8.6 in `ConnectionErrorProof` pipeline -- Fixed `ConnectionErrorProof` pipeline returning too few responses or throwing `ValueError` on read errors with a single connection ### Security - Fixed aborted cluster transactions leaving node connections in `MULTI` diff --git a/src/Pipeline/ConnectionErrorProof.php b/src/Pipeline/ConnectionErrorProof.php index 5a5b8e0..7059ac9 100644 --- a/src/Pipeline/ConnectionErrorProof.php +++ b/src/Pipeline/ConnectionErrorProof.php @@ -73,7 +73,7 @@ class ConnectionErrorProof extends Pipeline try { $responses[$i] = $connection->readResponse($command); } catch (CommunicationException $exception) { - $add = $sizeOfPipe - count($responses); + $add = count($commands) - count($responses); $responses = array_merge($responses, array_fill(0, $add, $exception)); break; @@ -101,10 +101,10 @@ class ConnectionErrorProof extends Pipeline $command = $commands->dequeue(); $cmdConnection = $connection->getConnectionByCommand($command); - $connectionId = spl_object_id($cmdConnection); + $connectionHash = spl_object_hash($cmdConnection); - if (isset($exceptions[$connectionId])) { - $responses[$i] = $exceptions[$connectionId]; + if (isset($exceptions[$connectionHash])) { + $responses[$i] = $exceptions[$connectionHash]; continue; } @@ -112,7 +112,7 @@ class ConnectionErrorProof extends Pipeline $responses[$i] = $cmdConnection->readResponse($command); } catch (CommunicationException $exception) { $responses[$i] = $exception; - $exceptions[$connectionId] = $exception; + $exceptions[$connectionHash] = $exception; } } diff --git a/tests/Predis/Pipeline/ConnectionErrorProofTest.php b/tests/Predis/Pipeline/ConnectionErrorProofTest.php deleted file mode 100644 index 01e448b..0000000 --- a/tests/Predis/Pipeline/ConnectionErrorProofTest.php +++ /dev/null @@ -1,216 +0,0 @@ -serializeCommand() . (new PING())->serializeCommand() . (new PING())->serializeCommand(); - - $connection = $this->getMockConnection('tcp://127.0.0.1:7001'); - $exception = new ConnectionException($connection, 'Connection failed'); - - $connection - ->expects($this->once()) - ->method('write') - ->with($buffer) - ->willThrowException($exception); - $connection - ->expects($this->never()) - ->method('readResponse'); - - $pipeline = new ConnectionErrorProof(new Client($connection)); - - $pipeline->ping(); - $pipeline->ping(); - $pipeline->ping(); - - $this->assertSame([$exception, $exception, $exception], $pipeline->execute()); - } - - /** - * @group disconnected - */ - public function testReturnsExceptionOfEachFailedConnectionWithClusterConnection(): void - { - $connection1 = $this->getMockConnection('tcp://127.0.0.1:7001'); - $exception1 = new ConnectionException($connection1, 'Connection to node 1 failed'); - $connection1 - ->expects($this->exactly(2)) - ->method('write'); - $connection1 - ->expects($this->once()) - ->method('readResponse') - ->willThrowException($exception1); - - $connection2 = $this->getMockConnection('tcp://127.0.0.1:7002'); - $exception2 = new ConnectionException($connection2, 'Connection to node 2 failed'); - $connection2 - ->expects($this->exactly(2)) - ->method('write'); - $connection2 - ->expects($this->once()) - ->method('readResponse') - ->willThrowException($exception2); - - $cluster = $this->getMockClusterConnection(['foo' => $connection1, 'bar' => $connection2]); - - $pipeline = new ConnectionErrorProof(new Client($cluster)); - - $pipeline->get('foo'); - $pipeline->get('bar'); - $pipeline->get('foo'); - $pipeline->get('bar'); - - $this->assertSame([$exception1, $exception2, $exception1, $exception2], $pipeline->execute()); - } - - /** - * @group disconnected - */ - public function testReadsResponsesFromHealthyConnectionsWhenOneFailsWithClusterConnection(): void - { - $connection1 = $this->getMockConnection('tcp://127.0.0.1:7001'); - $exception = new ConnectionException($connection1, 'Connection to node 1 failed'); - $connection1 - ->expects($this->exactly(2)) - ->method('write'); - $connection1 - ->expects($this->once()) - ->method('readResponse') - ->willThrowException($exception); - - $connection2 = $this->getMockConnection('tcp://127.0.0.1:7002'); - $connection2 - ->expects($this->exactly(2)) - ->method('write'); - $connection2 - ->expects($this->exactly(2)) - ->method('readResponse') - ->willReturnOnConsecutiveCalls('value1', 'value2'); - - $cluster = $this->getMockClusterConnection(['foo' => $connection1, 'bar' => $connection2]); - - $pipeline = new ConnectionErrorProof(new Client($cluster)); - - $pipeline->get('foo'); - $pipeline->get('bar'); - $pipeline->get('foo'); - $pipeline->get('bar'); - - $this->assertSame([$exception, 'value1', $exception, 'value2'], $pipeline->execute()); - } - - /** - * @group disconnected - */ - public function testThrowsExceptionOnUnsupportedConnection(): void - { - $this->expectException(NotSupportedException::class); - $this->expectExceptionMessageMatches('/^The connection class \'.+\' is not supported\.$/'); - - $connection = $this->getMockBuilder('Predis\Connection\Replication\ReplicationInterface')->getMock(); - - $pipeline = new ConnectionErrorProof(new Client($connection)); - - $pipeline->ping(); - $pipeline->execute(); - } - - /** - * @dataProvider singleConnectionReadFailureProvider - * @group disconnected - */ - public function testReturnsExceptionForFailedAndRemainingCommandsOnReadFailureWithSingleConnection( - int $sizeOfPipe, - int $failingIndex - ): void { - $connection = $this->getMockConnection('tcp://127.0.0.1:7001'); - $exception = new ConnectionException($connection, 'Connection lost'); - - $readResponses = []; - $expectedResponses = []; - - for ($i = 0; $i < $sizeOfPipe; ++$i) { - if ($i < $failingIndex) { - $readResponses[] = "value$i"; - $expectedResponses[] = "value$i"; - } else { - $expectedResponses[] = $exception; - } - } - - $readResponses[] = $this->throwException($exception); - - $connection - ->expects($this->once()) - ->method('write'); - $connection - ->expects($this->exactly($failingIndex + 1)) - ->method('readResponse') - ->willReturnOnConsecutiveCalls(...$readResponses); - - $pipeline = new ConnectionErrorProof(new Client($connection)); - - for ($i = 0; $i < $sizeOfPipe; ++$i) { - $pipeline->echo("value$i"); - } - - $responses = $pipeline->execute(); - - $this->assertCount($sizeOfPipe, $responses); - $this->assertSame($expectedResponses, $responses); - } - - public function singleConnectionReadFailureProvider(): array - { - return [ - 'single command' => [1, 0], - 'first of three' => [3, 0], - 'second of three' => [3, 1], - 'last of three' => [3, 2], - 'fourth of five' => [5, 3], - ]; - } - - /** - * Returns a mocked cluster connection routing commands by their first key. - * - * @param array $connectionsByKey Node connections indexed by key - * - * @return \PHPUnit\Framework\MockObject\MockObject|\Predis\Connection\Cluster\ClusterInterface - */ - private function getMockClusterConnection(array $connectionsByKey) - { - $cluster = $this->getMockBuilder('Predis\Connection\Cluster\ClusterInterface')->getMock(); - $cluster - ->expects($this->atLeastOnce()) - ->method('getConnectionByCommand') - ->willReturnCallback(static function (CommandInterface $command) use ($connectionsByKey) { - return $connectionsByKey[$command->getArgument(0)]; - }); - - return $cluster; - } -}