Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit eb35f9d

Browse files
committed
[DoctrineBridge] doctrine connection listener for long running runtime
1 parent 014bfac commit eb35f9d

File tree

3 files changed

+213
-0
lines changed

3 files changed

+213
-0
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Doctrine\Listener;
13+
14+
use Doctrine\DBAL\Connection;
15+
use Doctrine\DBAL\Exception as DBALException;
16+
use Doctrine\ORM\EntityManagerInterface;
17+
use Doctrine\Persistence\ManagerRegistry;
18+
use ProxyManager\Proxy\LazyLoadingInterface;
19+
use Symfony\Bridge\Doctrine\Event\ForceKernelRebootEvent;
20+
use Symfony\Component\DependencyInjection\ContainerInterface;
21+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
22+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
23+
use Symfony\Component\HttpKernel\Event\RequestEvent;
24+
use Symfony\Component\HttpKernel\KernelEvents;
25+
use Symfony\Component\VarExporter\LazyObjectInterface;
26+
27+
/**
28+
* Based on https://github.com/Baldinof/roadrunner-bundle/blob/3.x/src/Integration/Doctrine/DoctrineORMMiddleware.php.
29+
*/
30+
class DoctrineConnectionSubscriber implements EventSubscriberInterface
31+
{
32+
public function __construct(private ManagerRegistry $managerRegistry, private ContainerInterface $container, private EventDispatcherInterface $eventDispatcher)
33+
{
34+
}
35+
36+
public function onKernelRequest(RequestEvent $event)
37+
{
38+
$connectionServices = $this->managerRegistry->getConnectionNames();
39+
40+
foreach ($connectionServices as $connectionServiceName) {
41+
if (!$this->container->initialized($connectionServiceName)) {
42+
continue;
43+
}
44+
45+
$connection = $this->container->get($connectionServiceName);
46+
47+
if (!$connection instanceof Connection) {
48+
throw new \RuntimeException(sprintf('The value "%s" is not an instance of "%s".', $connection ? $connection::class : 'null', Connection::class));
49+
}
50+
51+
if ($connection->isConnected() && !$this->ping($connection)) {
52+
$connection->close();
53+
}
54+
55+
$managerNames = $this->managerRegistry->getManagerNames();
56+
57+
foreach ($managerNames as $managerName) {
58+
if (!$this->container->initialized($managerName)) {
59+
continue;
60+
}
61+
62+
$manager = $this->container->get($managerName);
63+
64+
if (!$manager instanceof EntityManagerInterface) {
65+
throw new \RuntimeException(sprintf('The value "%s" is not an instance of "%s".', $manager ? $manager::class : 'null', EntityManagerInterface::class));
66+
}
67+
68+
if ($manager instanceof LazyLoadingInterface || $manager instanceof LazyObjectInterface) {
69+
continue; // Doctrine bundle will handle manager reset on next request
70+
}
71+
72+
if (!$manager->isOpen()) {
73+
$this->eventDispatcher->dispatch(new ForceKernelRebootEvent(
74+
sprintf('Entity manager "%s" is closed and the package `symfony/proxy-manager-bridge` is not installed so kernel reset will not re-open it', $managerName)
75+
));
76+
77+
return;
78+
}
79+
}
80+
}
81+
}
82+
83+
private function ping(Connection $con): bool
84+
{
85+
try {
86+
$con->executeQuery($con->getDatabasePlatform()->getDummySelectSQL());
87+
88+
return true;
89+
} catch (DBALException $e) {
90+
return false;
91+
}
92+
}
93+
94+
public static function getSubscribedEvents(): array
95+
{
96+
return [
97+
KernelEvents::REQUEST => 'onKernelRequest',
98+
];
99+
}
100+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
namespace Symfony\Bridge\Doctrine\Middleware;
4+
5+
use Doctrine\DBAL\Connection;
6+
use Doctrine\DBAL\Driver;
7+
use Doctrine\DBAL\Driver\Connection as DriverConnection;
8+
use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware;
9+
use Doctrine\DBAL\Exception as DBALException;
10+
use Doctrine\ORM\EntityManagerInterface;
11+
use Doctrine\Persistence\ManagerRegistry;
12+
use Symfony\Component\DependencyInjection\ContainerInterface;
13+
14+
class DoctrineConnectionDriver extends AbstractDriverMiddleware
15+
{
16+
public function __construct(private Driver $driver, private readonly ManagerRegistry $managerRegistry, private readonly ContainerInterface $container)
17+
{
18+
parent::__construct($driver);
19+
}
20+
21+
public function connect(array $params): DriverConnection
22+
{
23+
$connectionServices = $this->managerRegistry->getConnectionNames();
24+
25+
foreach ($connectionServices as $connectionServiceName) {
26+
27+
if (! $this->container->initialized($connectionServiceName)) {
28+
continue;
29+
}
30+
31+
$connection = $this->container->get($connectionServiceName);
32+
33+
if (!$connection instanceof Connection) {
34+
continue;
35+
}
36+
37+
if ($connection->isConnected()) {
38+
$this->pingConnection($connection);
39+
}
40+
41+
$managerNames = $this->managerRegistry->getManagerNames();
42+
43+
foreach ($managerNames as $managerName) {
44+
if (!$this->container->initialized($managerName)) {
45+
continue;
46+
}
47+
48+
$manager = $this->container->get($managerName);
49+
50+
if (!$manager instanceof EntityManagerInterface) {
51+
continue;
52+
}
53+
54+
if (!$manager->isOpen()) {
55+
$this->managerRegistry->resetManager($managerName);
56+
}
57+
}
58+
}
59+
60+
return parent::connect($params);
61+
}
62+
63+
private function pingConnection(Connection $connection): void
64+
{
65+
try {
66+
$this->executeDummySql($connection);
67+
} catch (DBALException $e) {
68+
$connection->close();
69+
// Attempt to reestablish the lazy connection by sending another query.
70+
$this->executeDummySql($connection);
71+
}
72+
}
73+
74+
/**
75+
* @throws DBALException
76+
*/
77+
private function executeDummySql(Connection $connection): void
78+
{
79+
$connection->executeQuery($connection->getDatabasePlatform()->getDummySelectSQL());
80+
}
81+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Doctrine\Middleware;
13+
14+
use Doctrine\DBAL\Driver;
15+
use Doctrine\Persistence\ManagerRegistry;
16+
use Symfony\Component\DependencyInjection\ContainerInterface;
17+
use Doctrine\DBAL\Driver\Middleware;
18+
19+
/**
20+
* Based on https://github.com/Baldinof/roadrunner-bundle/blob/3.x/src/Integration/Doctrine/DoctrineORMMiddleware.php.
21+
*/
22+
class DoctrineConnectionMiddleware implements Middleware
23+
{
24+
public function __construct(private ManagerRegistry $managerRegistry, private ContainerInterface $container)
25+
{
26+
}
27+
28+
public function wrap(Driver $driver): Driver
29+
{
30+
return new DoctrineConnectionDriver($driver, $this->managerRegistry, $this->container);
31+
}
32+
}

0 commit comments

Comments
 (0)