Skip to content

Commit

Permalink
take into account call to db
Browse files Browse the repository at this point in the history
  • Loading branch information
alli83 committed Apr 22, 2024
1 parent eadaa66 commit e10e349
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 26 deletions.
3 changes: 1 addition & 2 deletions DependencyInjection/Configuration.php
Expand Up @@ -221,10 +221,9 @@ private function getDbalConnectionsNode(): ArrayNodeDefinition
->end()
->booleanNode('disable_type_comments')->end()
->scalarNode('server_version')->end()
->integerNode('check_connection_timing')->end()
->integerNode('idle_connection_ttl')->defaultValue(600)->end()
->scalarNode('driver_class')->end()
->scalarNode('wrapper_class')->end()
->scalarNode('check_connection_frequency')->defaultValue(30)->end()
->booleanNode('keep_slave')
->setDeprecated(
'doctrine/doctrine-bundle',
Expand Down
47 changes: 25 additions & 22 deletions DependencyInjection/DoctrineExtension.php
Expand Up @@ -12,6 +12,7 @@
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\IdGeneratorPass;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface;
use Doctrine\Bundle\DoctrineBundle\Middleware\IdleConnectionMiddleware;

Check failure on line 15 in DependencyInjection/DoctrineExtension.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Type Doctrine\Bundle\DoctrineBundle\Middleware\IdleConnectionMiddleware is not used in this file.
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface;
use Doctrine\Common\Annotations\Annotation;
use Doctrine\DBAL\Connection;
Expand All @@ -37,6 +38,7 @@
use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension;
use Symfony\Bridge\Doctrine\IdGenerator\UlidGenerator;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
use Symfony\Bridge\Doctrine\Middleware\IdleConnection\Listener;
use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor;
use Symfony\Bridge\Doctrine\SchemaListener\DoctrineDbalCacheAdapterSchemaListener;
use Symfony\Bridge\Doctrine\SchemaListener\LockStoreSchemaListener;
Expand Down Expand Up @@ -83,7 +85,7 @@
*
* @final since 2.9
* @psalm-type DBALConfig = array{
* connections: array<string, array{logging: bool, profiling: bool, profiling_collect_backtrace: bool}>,
* connections: array<string, array{logging: bool, profiling: bool, profiling_collect_backtrace: bool, idle_connection_ttl: int}>,
* driver_schemes: array<string, string>,
* default_connection: string,
* types: array<string, string>,
Expand Down Expand Up @@ -174,12 +176,6 @@ protected function dbalLoad(array $config, ContainerBuilder $container)
$config['default_connection'] = reset($keys);
}

if (! $container->hasParameter('kernel.runtime_mode') && ! $container->hasParameter('kernel.runtime_mode.worker')) {
$container->removeDefinition('doctrine.listeners.doctrine_connection_listener');
} else {
$container->getDefinition('doctrine.orm.listeners.doctrine_connection_listener')->setArgument(1, $config['check_connection_timing']);
}

$this->defaultConnection = $config['default_connection'];

$container->setAlias('database_connection', sprintf('doctrine.dbal.%s_connection', $this->defaultConnection));
Expand All @@ -202,13 +198,8 @@ protected function dbalLoad(array $config, ContainerBuilder $container)
$connWithLogging = [];
$connWithProfiling = [];
$connWithBacktrace = [];
$timingByConnection = [];
$skipTiming = false;

if (! $container->hasParameter('kernel.runtime_mode') && ! $container->hasParameter('kernel.runtime_mode.worker')) {
$container->removeDefinition('doctrine.listeners.doctrine_connection_listener');
$skipTiming = true;
}
$ttlByConnection = [];
$connWithTtl = [];

foreach ($config['connections'] as $name => $connection) {
if ($connection['logging']) {
Expand All @@ -223,17 +214,14 @@ protected function dbalLoad(array $config, ContainerBuilder $container)
}
}

if (! $skipTiming) {
$timingByConnection[] = ['name' => $connections[$name], 'timing' => $connection['check_connection_timing']];
if ($connection['idle_connection_ttl']) {
$connWithTtl[] = $name;
$ttlByConnection[$name] = $connection['idle_connection_ttl'];
}

$this->loadDbalConnection($name, $connection, $container);
}

if (! $skipTiming) {
$container->getDefinition('doctrine.orm.listeners.doctrine_connection_listener')->setArgument(1, $timingByConnection);
}

$container->registerForAutoconfiguration(MiddlewareInterface::class)->addTag('doctrine.middleware');

$container->registerAttributeForAutoconfiguration(AsMiddleware::class, static function (ChildDefinition $definition, AsMiddleware $attribute) {
Expand All @@ -250,7 +238,14 @@ protected function dbalLoad(array $config, ContainerBuilder $container)
}
});

$this->registerDbalMiddlewares($container, $connWithLogging, $connWithProfiling, $connWithBacktrace);
$this->registerDbalMiddlewares($container, $connWithLogging, $connWithProfiling, $connWithBacktrace, $connWithTtl);

$container->getDefinition('doctrine.dbal.idle_connection_middleware')->setArgument(1, $ttlByConnection);

if (! class_exists(Listener::class)) {

Check failure on line 245 in DependencyInjection/DoctrineExtension.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Use early exit to reduce code nesting.
$container->removeDefinition('doctrine.dbal.idle_connection_listener');
$container->removeDefinition('doctrine.dbal.idle_connection_middleware');
}
}

/**
Expand Down Expand Up @@ -1196,12 +1191,14 @@ private function createArrayAdapterCachePool(ContainerBuilder $container, string
* @param string[] $connWithLogging
* @param string[] $connWithProfiling
* @param string[] $connWithBacktrace
* @param string[] $connWithTtl
*/
private function registerDbalMiddlewares(
ContainerBuilder $container,
array $connWithLogging,
array $connWithProfiling,
array $connWithBacktrace
array $connWithBacktrace,
array $connWithTtl
): void {
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('middlewares.xml');
Expand All @@ -1217,5 +1214,11 @@ private function registerDbalMiddlewares(
$debugMiddlewareAbstractDef
->addTag('doctrine.middleware', ['connection' => $connName]);
}

$idleConnectionMiddlewareAbstractDef = $container->getDefinition('doctrine.dbal.idle_connection_middleware');
foreach ($connWithTtl as $connName) {
$idleConnectionMiddlewareAbstractDef
->addTag('doctrine.middleware', ['connection' => $connName, 'priority' => 10]);
}
}
}
36 changes: 36 additions & 0 deletions Middleware/IdleConnectionMiddleware.php
@@ -0,0 +1,36 @@
<?php

namespace Doctrine\Bundle\DoctrineBundle\Middleware;

use ArrayObject;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Middleware;
use Symfony\Bridge\Doctrine\Middleware\IdleConnection\Driver as IdleConnectionDriver;

class IdleConnectionMiddleware implements Middleware, ConnectionNameAwareInterface
{
private ArrayObject $connectionExpiries;
/** @var array<string, int> */
private array $ttlByConnection;
private string $connectionName;

/**
* @param ArrayObject<string, int> $connectionExpiries
* @param array<string, int> $ttlByConnection

Check failure on line 19 in Middleware/IdleConnectionMiddleware.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Expected 7 spaces after parameter type; 1 found
*/
public function __construct(ArrayObject $connectionExpiries, array $ttlByConnection)
{
$this->connectionExpiries = $connectionExpiries;
$this->ttlByConnection = $ttlByConnection;
}

public function setConnectionName(string $name): void
{
$this->connectionName = $name;
}

public function wrap(Driver $driver): Driver
{
return new IdleConnectionDriver($driver, $this->connectionExpiries, $this->ttlByConnection[$this->connectionName], $this->connectionName);

Check failure on line 34 in Middleware/IdleConnectionMiddleware.php

View workflow job for this annotation

GitHub Actions / Static Analysis with Psalm

UndefinedClass

Middleware/IdleConnectionMiddleware.php:34:20: UndefinedClass: Class, interface or enum named Symfony\Bridge\Doctrine\Middleware\IdleConnection\Driver does not exist (see https://psalm.dev/019)
}
}
4 changes: 2 additions & 2 deletions Resources/config/dbal.xml
Expand Up @@ -101,9 +101,9 @@
<tag name="controller.service_arguments" />
</service>

<service id="doctrine.listeners.doctrine_connection_listener" class="Symfony\Bridge\Doctrine\Listener\ConnectionListener">
<service id="doctrine.dbal.idle_connection_listener" class="Symfony\Bridge\Doctrine\Middleware\IdleConnection\Listener">
<argument type="service" id="doctrine.dbal.connection_expiries" />
<argument type="service" id="service_container" />
<argument /> <!-- check timing -->
<tag name="kernel.event_subscriber" />
</service>

Expand Down
5 changes: 5 additions & 0 deletions Resources/config/middlewares.xml
Expand Up @@ -5,6 +5,7 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
<service id="doctrine.dbal.connection_expiries" class="ArrayObject"/>
<service id="doctrine.dbal.logging_middleware" class="Doctrine\DBAL\Logging\Middleware" abstract="true">
<argument type="service" id="logger" />
<tag name="monolog.logger" channel="doctrine" />
Expand All @@ -17,5 +18,9 @@
<argument type="service" id="doctrine.debug_data_holder" />
<argument type="service" id="debug.stopwatch" on-invalid="null" />
</service>
<service id="doctrine.dbal.idle_connection_middleware" class="Doctrine\Bundle\DoctrineBundle\Middleware\IdleConnectionMiddleware" abstract="true">
<argument type="service" id="doctrine.dbal.connection_expiries" />
<argument /> <!-- check timing -->
</service>
</services>
</container>
25 changes: 25 additions & 0 deletions Tests/Middleware/IdleConnectionMiddlewareTest.php
@@ -0,0 +1,25 @@
<?php

namespace Doctrine\Bundle\DoctrineBundle\Tests\Middleware;

use Doctrine\Bundle\DoctrineBundle\Middleware\IdleConnectionMiddleware;
use Doctrine\DBAL\Driver;
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\Doctrine\Middleware\IdleConnection\Driver as IdleConnectionDriver;

class IdleConnectionMiddlewareTest extends TestCase
{
public function testWrap()
{
$connectionExpiries = new \ArrayObject(['connectionone' => time() - 30, 'connectiontwo' => time() + 40]);

Check failure on line 14 in Tests/Middleware/IdleConnectionMiddlewareTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Class \ArrayObject should not be referenced via a fully qualified name, but via a use statement.

Check failure on line 14 in Tests/Middleware/IdleConnectionMiddlewareTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Function time() should not be referenced via a fallback global name, but via a use statement.

Check failure on line 14 in Tests/Middleware/IdleConnectionMiddlewareTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Function time() should not be referenced via a fallback global name, but via a use statement.
$ttlByConnection = ['connectionone' => 25, 'connectiontwo' => 60];

Check failure on line 15 in Tests/Middleware/IdleConnectionMiddlewareTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

$middleware = new IdleConnectionMiddleware($connectionExpiries, $ttlByConnection);

Check failure on line 17 in Tests/Middleware/IdleConnectionMiddlewareTest.php

View workflow job for this annotation

GitHub Actions / Static Analysis with Psalm

InvalidArgument

Tests/Middleware/IdleConnectionMiddlewareTest.php:17:52: InvalidArgument: Argument 1 of Doctrine\Bundle\DoctrineBundle\Middleware\IdleConnectionMiddleware::__construct expects ArrayObject<string, int>, but ArrayObject<'connectionone'|'connectiontwo', int<-29, max>> provided (see https://psalm.dev/004)
$middleware->setConnectionName('connectionone');

$driverMock = $this->createMock(Driver::class);

Check failure on line 20 in Tests/Middleware/IdleConnectionMiddlewareTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space
$wrappedDriver = $middleware->wrap($driverMock);

$this->assertInstanceOf(IdleConnectionDriver::class, $wrappedDriver);

Check failure on line 23 in Tests/Middleware/IdleConnectionMiddlewareTest.php

View workflow job for this annotation

GitHub Actions / Static Analysis with Psalm

UndefinedClass

Tests/Middleware/IdleConnectionMiddlewareTest.php:23:33: UndefinedClass: Class, interface or enum named Symfony\Bridge\Doctrine\Middleware\IdleConnection\Driver does not exist (see https://psalm.dev/019)
}
}

0 comments on commit e10e349

Please sign in to comment.