Retry all exceptions from Sentinel replicas (#1577)

* Retry SentinelReplication::retryCommandOnFailure on all classes with Throwable interface

* Retry SentinelReplication::retryCommandOnFailure on all classes with Throwable interface

* Fix coding style
This commit is contained in:
ridgey-dev
2025-07-18 08:21:40 +02:00
committed by GitHub
parent c60003a717
commit 8e7afbe679
3 changed files with 98 additions and 4 deletions
+1
View File
@@ -11,6 +11,7 @@
### Changed
- Handle and retry `LOADING` errors from Sentinel replicas (#1536)
- Retry all exceptions from Sentinel replicas (#1577)
### Fixed
- Fixed PHP 8.4 deprecated call to `stream_context_set_option()` (#1545)
@@ -16,7 +16,6 @@ use InvalidArgumentException;
use Predis\Command\Command;
use Predis\Command\CommandInterface;
use Predis\Command\RawCommand;
use Predis\CommunicationException;
use Predis\Connection\AbstractAggregateConnection;
use Predis\Connection\ConnectionException;
use Predis\Connection\FactoryInterface as ConnectionFactoryInterface;
@@ -28,6 +27,7 @@ use Predis\Replication\RoleException;
use Predis\Response\Error;
use Predis\Response\ErrorInterface as ErrorResponseInterface;
use Predis\Response\ServerException;
use Throwable;
/**
* @author Daniele Alessandri <suppakilla@gmail.com>
@@ -719,9 +719,12 @@ class SentinelReplication extends AbstractAggregateConnection implements Replica
throw new ConnectionException($this->current, $response->getMessage());
}
break;
} catch (CommunicationException $exception) {
} catch (Throwable $exception) {
$this->wipeServerList();
$exception->getConnection()->disconnect();
if ($exception instanceof ConnectionException) {
$exception->getConnection()->disconnect();
}
if ($retries === $this->retryLimit) {
throw $exception;
@@ -12,6 +12,7 @@
namespace Predis\Connection\Replication;
use Exception;
use PHPUnit\Framework\MockObject\MockObject;
use Predis\Command;
use Predis\Connection;
@@ -1193,6 +1194,95 @@ class SentinelReplicationTest extends PredisTestCase
$this->assertSame($cmdSetResponse, $replication->executeCommand($cmdSet));
}
/**
* @group disconnected
*/
public function testMethodExecuteCommandRetriesReadOnlyCommandOnNextSlaveOnConnectionException(): void
{
$sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
$sentinel1
->expects($this->any())
->method('executeCommand')
->with($this->isRedisCommand(
'SENTINEL', ['slaves', 'svc']
))
->willReturn(
[
[
'name', '127.0.0.1:6383',
'ip', '127.0.0.1',
'port', '6383',
'runid', '1c0bf1291797fbc5608c07a17da394147dc62817',
'flags', 'slave',
'master-host', '127.0.0.1',
'master-port', '6381',
],
]
);
$master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
$master
->expects($this->any())
->method('isConnected')
->willReturn(true);
$slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
$slave1
->expects($this->any())
->method('isConnected')
->willReturn(true);
$slave1
->expects($this->any())
->method('disconnect');
$slave1
->expects($this->once())
->method('executeCommand')
->with(
$this->isRedisCommand('GET', ['key'])
)
->willThrowException(
new Connection\ConnectionException($slave1, 'Unknown connection error [127.0.0.1:6382]')
);
$slave2 = $this->getMockConnection('tcp://127.0.0.1:6383?role=slave');
$slave2
->expects($this->any())
->method('isConnected')
->willReturn(true);
$slave2
->expects($this->once())
->method('executeCommand')
->withConsecutive(
[$this->isRedisCommand('GET', ['key'])]
)
->willReturnOnConsecutiveCalls(
'value'
);
/** @var Connection\FactoryInterface|MockObject */
$factory = $this->getMockBuilder('Predis\Connection\FactoryInterface')->getMock();
$factory
->expects($this->once())
->method('create')
->with([
'host' => '127.0.0.1',
'port' => '6383',
'role' => 'slave',
])
->willReturn($slave2);
$replication = $this->getReplicationConnection('svc', [$sentinel1], $factory);
$replication->add($master);
$replication->add($slave1);
self::assertSame('value', $replication->executeCommand(
Command\RawCommand::create('get', 'key')
));
}
/**
* @group disconnected
*/
@@ -1237,7 +1327,7 @@ class SentinelReplicationTest extends PredisTestCase
$this->isRedisCommand('GET', ['key'])
)
->willThrowException(
new Connection\ConnectionException($slave1, 'Unknown connection error [127.0.0.1:6382]')
new Exception('message')
);
$slave2 = $this->getMockConnection('tcp://127.0.0.1:6383?role=slave');