From: Christoph Wurst Date: Wed, 24 Apr 2024 09:41:30 +0000 (+0200) Subject: fix(db): Prevent two connections for single node databases X-Git-Tag: v29.0.2rc1~10^2 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=refs%2Fpull%2F45541%2Fhead;p=nextcloud-server.git fix(db): Prevent two connections for single node databases Signed-off-by: Christoph Wurst --- diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index bd602c27222..e636585ac35 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -57,6 +57,7 @@ use OCP\PreConditionNotMetException; use OCP\Profiler\IProfiler; use Psr\Clock\ClockInterface; use Psr\Log\LoggerInterface; +use function count; use function in_array; class Connection extends PrimaryReadReplicaConnection { @@ -96,7 +97,7 @@ class Connection extends PrimaryReadReplicaConnection { * @throws \Exception */ public function __construct( - array $params, + private array $params, Driver $driver, ?Configuration $config = null, ?EventManager $eventManager = null @@ -156,6 +157,15 @@ class Connection extends PrimaryReadReplicaConnection { } } + protected function performConnect(?string $connectionName = null): bool { + if (($connectionName ?? 'replica') === 'replica' + && count($this->params['replica']) === 1 + && $this->params['primary'] === $this->params['replica'][0]) { + return parent::performConnect('primary'); + } + return parent::performConnect($connectionName); + } + public function getStats(): array { return [ 'built' => $this->queriesBuilt, diff --git a/tests/lib/DB/ConnectionTest.php b/tests/lib/DB/ConnectionTest.php new file mode 100644 index 00000000000..3f06082ff0c --- /dev/null +++ b/tests/lib/DB/ConnectionTest.php @@ -0,0 +1,101 @@ + 'test', + 'password' => 'topsecret', + 'host' => 'test', + ]; + $adapter = $this->createMock(Adapter::class); + $driver = $this->createMock(Driver::class); + $configuration = $this->createMock(Configuration::class); + $connection = $this->getMockBuilder(Connection::class) + ->onlyMethods(['connectTo']) + ->setConstructorArgs([ + [ + 'adapter' => $adapter, + 'platform' => new MySQLPlatform(), + 'tablePrefix' => 'nctest', + 'primary' => $connectionParams, + 'replica' => [ + $connectionParams, + ], + ], + $driver, + $configuration, + ]) + ->getMock(); + $driverConnection = $this->createMock(DriverConnection::class); + $connection->expects(self::once()) + ->method('connectTo') + ->with('primary') + ->willReturn($driverConnection); + + $connection->ensureConnectedToReplica(); + $connection->ensureConnectedToPrimary(); + $connection->ensureConnectedToReplica(); + } + + public function testClusterConnectsToPrimaryAndReplica(): void { + $connectionParamsPrimary = [ + 'user' => 'test', + 'password' => 'topsecret', + 'host' => 'testprimary', + ]; + $connectionParamsReplica = [ + 'user' => 'test', + 'password' => 'topsecret', + 'host' => 'testreplica', + ]; + $adapter = $this->createMock(Adapter::class); + $driver = $this->createMock(Driver::class); + $configuration = $this->createMock(Configuration::class); + $connection = $this->getMockBuilder(Connection::class) + ->onlyMethods(['connectTo']) + ->setConstructorArgs([ + [ + 'adapter' => $adapter, + 'platform' => new MySQLPlatform(), + 'tablePrefix' => 'nctest', + 'primary' => $connectionParamsPrimary, + 'replica' => [ + $connectionParamsReplica, + ], + ], + $driver, + $configuration, + ]) + ->getMock(); + $driverConnection = $this->createMock(DriverConnection::class); + $connection->expects(self::exactly(2)) + ->method('connectTo') + ->willReturn($driverConnection); + + $connection->ensureConnectedToReplica(); + $connection->ensureConnectedToPrimary(); + $connection->ensureConnectedToReplica(); + } + +}