mirror of
https://github.com/nextcloud/server.git
synced 2026-06-10 09:13:19 -04:00
Merge pull request #45013 from nextcloud/fix/db/two-primary-connections-transaction-isolation-level
fix(db): Prevent two connections for single node databases
This commit is contained in:
commit
4a3bb05038
2 changed files with 112 additions and 1 deletions
|
|
@ -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,
|
||||
|
|
|
|||
101
tests/lib/DB/ConnectionTest.php
Normal file
101
tests/lib/DB/ConnectionTest.php
Normal file
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue