From 3bfba2042c0397452193e9b6f1fa4bd1596b17a6 Mon Sep 17 00:00:00 2001 From: Christoph Wurst Date: Wed, 24 Apr 2024 11:41:30 +0200 Subject: [PATCH] fix(db): Prevent two connections for single node databases Signed-off-by: Christoph Wurst --- lib/private/DB/Connection.php | 12 +++- tests/lib/DB/ConnectionTest.php | 101 ++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 tests/lib/DB/ConnectionTest.php diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index b748312d156..8d300123a40 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -31,6 +31,7 @@ use OCP\Profiler\IProfiler; use OCP\Server; use Psr\Clock\ClockInterface; use Psr\Log\LoggerInterface; +use function count; use function in_array; class Connection extends PrimaryReadReplicaConnection { @@ -73,7 +74,7 @@ class Connection extends PrimaryReadReplicaConnection { * @throws \Exception */ public function __construct( - array $params, + private array $params, Driver $driver, ?Configuration $config = null, ?EventManager $eventManager = null @@ -138,6 +139,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(); + } + +} -- 2.39.5