]> source.dussan.org Git - nextcloud-server.git/commitdiff
fix(db): Prevent two connections for single node databases 45541/head
authorChristoph Wurst <christoph@winzerhof-wurst.at>
Wed, 24 Apr 2024 09:41:30 +0000 (11:41 +0200)
committerbackportbot[bot] <backportbot[bot]@users.noreply.github.com>
Tue, 28 May 2024 09:45:18 +0000 (09:45 +0000)
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
lib/private/DB/Connection.php
tests/lib/DB/ConnectionTest.php [new file with mode: 0644]

index bd602c27222d5aee4b3952de8893105ab5f654b9..e636585ac3586182c3328e6bb18829b9a6b3612f 100644 (file)
@@ -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 (file)
index 0000000..3f06082
--- /dev/null
@@ -0,0 +1,101 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Test\DB;
+
+use Doctrine\DBAL\Configuration;
+use Doctrine\DBAL\Driver;
+use Doctrine\DBAL\Driver\Connection as DriverConnection;
+use Doctrine\DBAL\Platforms\MySQLPlatform;
+use OC\DB\Adapter;
+use OC\DB\Connection;
+use Test\TestCase;
+
+/**
+ * @group DB
+ */
+class ConnectionTest extends TestCase {
+
+       public function testSingleNodeConnectsToPrimaryOnly(): void {
+               $connectionParams = [
+                       'user' => '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();
+       }
+
+}