From 8cf73891c04e555e73406ff8b246886d56c6503b Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Mon, 3 Mar 2014 00:57:25 +0100 Subject: [PATCH 1/3] Add reconnectable connection --- lib/Alchemy/Phrasea/Command/Setup/Install.php | 2 +- .../Controller/Thesaurus/Thesaurus.php | 2 +- .../Core/Connection/ConnectionProvider.php | 4 +- .../Connection/ReconnectableConnection.php | 146 ++++++++++++++++++ .../Phrasea/Model/Manager/UserManager.php | 2 +- lib/Alchemy/Phrasea/Setup/Installer.php | 2 +- lib/classes/base.php | 2 +- lib/classes/collection.php | 2 +- lib/classes/databox.php | 2 +- lib/classes/databox/field.php | 2 +- lib/classes/patchthesaurus/100.php | 2 +- lib/classes/patchthesaurus/200.php | 2 +- lib/classes/patchthesaurus/201.php | 2 +- lib/classes/patchthesaurus/202.php | 2 +- lib/classes/patchthesaurus/203.php | 2 +- lib/classes/patchthesaurus/204.php | 2 +- lib/classes/patchthesaurus/interface.php | 2 +- .../Phrasea/Command/Setup/InstallTest.php | 2 +- .../Connection/ConnectionProviderTest.php | 17 ++ tests/classes/collectionTest.php | 2 +- tests/classes/databox/fieldTest.php | 4 +- 21 files changed, 184 insertions(+), 21 deletions(-) create mode 100644 lib/Alchemy/Phrasea/Core/Connection/ReconnectableConnection.php create mode 100644 tests/Alchemy/Tests/Phrasea/Core/Connection/ConnectionProviderTest.php diff --git a/lib/Alchemy/Phrasea/Command/Setup/Install.php b/lib/Alchemy/Phrasea/Command/Setup/Install.php index e07726088f..49691cf9f4 100644 --- a/lib/Alchemy/Phrasea/Command/Setup/Install.php +++ b/lib/Alchemy/Phrasea/Command/Setup/Install.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\Command\Setup; use Alchemy\Phrasea\Command\Command; -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; diff --git a/lib/Alchemy/Phrasea/Controller/Thesaurus/Thesaurus.php b/lib/Alchemy/Phrasea/Controller/Thesaurus/Thesaurus.php index 62677bb1eb..5d3b15670f 100644 --- a/lib/Alchemy/Phrasea/Controller/Thesaurus/Thesaurus.php +++ b/lib/Alchemy/Phrasea/Controller/Thesaurus/Thesaurus.php @@ -11,7 +11,7 @@ namespace Alchemy\Phrasea\Controller\Thesaurus; -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection; use Silex\Application; use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/lib/Alchemy/Phrasea/Core/Connection/ConnectionProvider.php b/lib/Alchemy/Phrasea/Core/Connection/ConnectionProvider.php index b236201fc2..07000ea6f2 100644 --- a/lib/Alchemy/Phrasea/Core/Connection/ConnectionProvider.php +++ b/lib/Alchemy/Phrasea/Core/Connection/ConnectionProvider.php @@ -13,7 +13,7 @@ namespace Alchemy\Phrasea\Core\Connection; use Doctrine\Common\EventManager; use Doctrine\DBAL\Configuration; -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\DriverManager; class ConnectionProvider @@ -58,6 +58,6 @@ class ConnectionProvider return $this->connections[$key]; } - return $this->connections[$key] = DriverManager::getConnection($params, $this->config, $this->eventManager);; + return $this->connections[$key] = new ReconnectableConnection(DriverManager::getConnection($params, $this->config, $this->eventManager)); } } diff --git a/lib/Alchemy/Phrasea/Core/Connection/ReconnectableConnection.php b/lib/Alchemy/Phrasea/Core/Connection/ReconnectableConnection.php new file mode 100644 index 0000000000..185a3ad900 --- /dev/null +++ b/lib/Alchemy/Phrasea/Core/Connection/ReconnectableConnection.php @@ -0,0 +1,146 @@ +connection = $connection; + } + + /** + * {@inheritdoc} + */ + function prepare($prepareString) + { + return $this->tryMethod(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + function query() + { + return $this->tryMethod(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + function quote($input, $type=\PDO::PARAM_STR) + { + return $this->tryMethod(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + function exec($statement) + { + return $this->tryMethod(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + function lastInsertId($name = null) + { + return $this->tryMethod(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + function beginTransaction() + { + return $this->tryMethod(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + function commit() + { + return $this->tryMethod(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + function rollBack() + { + return $this->tryMethod(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + function errorCode() + { + return $this->tryMethod(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + function errorInfo() + { + return $this->tryMethod(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function __call($method, $args) + { + return $this->tryMethod($method, $args); + } + + private function tryMethod($method, $args) + { + try { + set_error_handler(function ($errno, $errstr) { throw new \Exception($errstr, $errno); }); + $ret = call_user_func_array([$this->connection, $method], $args); + restore_error_handler(); + + return $ret; + } catch (\Exception $exception) { + restore_error_handler(); + $e = $exception; + while ($e->getPrevious() && !$e instanceof \PDOException) { + $e = $e->getPrevious(); + } + if ($e instanceof \PDOException && $e->errorInfo[1] == self::MYSQL_CONNECTION_TIMED_WAIT_CODE) { + $this->connection->close(); + $this->connection->connect(); + + return call_user_func_array([$this->connection, $method], $args); + } + if ((false !== strpos($exception->getMessage(), 'MySQL server has gone away')) || (false !== strpos($exception->getMessage(), 'errno=32 Broken pipe'))) { + $this->connection->close(); + $this->connection->connect(); + + return call_user_func_array([$this->connection, $method], $args); + } + + throw $e; + } + } +} diff --git a/lib/Alchemy/Phrasea/Model/Manager/UserManager.php b/lib/Alchemy/Phrasea/Model/Manager/UserManager.php index 6d99f8893a..ea83c62d67 100644 --- a/lib/Alchemy/Phrasea/Model/Manager/UserManager.php +++ b/lib/Alchemy/Phrasea/Model/Manager/UserManager.php @@ -14,7 +14,7 @@ namespace Alchemy\Phrasea\Model\Manager; use Doctrine\Common\Persistence\ObjectManager; use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\Model\Entities\UserSetting; -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection; use Doctrine\ORM\UnitOfWork AS UOW; class UserManager diff --git a/lib/Alchemy/Phrasea/Setup/Installer.php b/lib/Alchemy/Phrasea/Setup/Installer.php index 6b2d6ccb02..40b7600337 100644 --- a/lib/Alchemy/Phrasea/Setup/Installer.php +++ b/lib/Alchemy/Phrasea/Setup/Installer.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\Setup; use Alchemy\Phrasea\Application; -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\DBALException; use Doctrine\ORM\Tools\SchemaTool; use Alchemy\Phrasea\Model\Entities\User; diff --git a/lib/classes/base.php b/lib/classes/base.php index b75c35384e..4cd131c7f4 100644 --- a/lib/classes/base.php +++ b/lib/classes/base.php @@ -13,7 +13,7 @@ use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Core\Version as PhraseaVersion; use vierbergenlars\SemVer\version; use Alchemy\Phrasea\Model\Entities\User; -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection; abstract class base implements cache_cacheableInterface { diff --git a/lib/classes/collection.php b/lib/classes/collection.php index 30d3fa8083..0671c3c417 100644 --- a/lib/classes/collection.php +++ b/lib/classes/collection.php @@ -12,7 +12,7 @@ use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Exception\InvalidArgumentException; use Alchemy\Phrasea\Model\Entities\User; -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection; class collection implements cache_cacheableInterface { diff --git a/lib/classes/databox.php b/lib/classes/databox.php index 253e7e4fae..8d30cb6ae1 100644 --- a/lib/classes/databox.php +++ b/lib/classes/databox.php @@ -12,7 +12,7 @@ use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\Exception\InvalidArgumentException; -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Translation\TranslatorInterface; diff --git a/lib/classes/databox/field.php b/lib/classes/databox/field.php index f06b546c25..f521ade653 100644 --- a/lib/classes/databox/field.php +++ b/lib/classes/databox/field.php @@ -13,7 +13,7 @@ use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Vocabulary; use Alchemy\Phrasea\Vocabulary\ControlProvider\ControlProviderInterface; use Alchemy\Phrasea\Metadata\Tag\Nosource; -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection; use PHPExiftool\Driver\TagInterface; use PHPExiftool\Driver\TagFactory; use PHPExiftool\Exception\TagUnknown; diff --git a/lib/classes/patchthesaurus/100.php b/lib/classes/patchthesaurus/100.php index cf622e9fd1..09934b8b35 100644 --- a/lib/classes/patchthesaurus/100.php +++ b/lib/classes/patchthesaurus/100.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection; class patchthesaurus_100 implements patchthesaurus_interface { diff --git a/lib/classes/patchthesaurus/200.php b/lib/classes/patchthesaurus/200.php index bf55ae546e..ca6b3817b9 100644 --- a/lib/classes/patchthesaurus/200.php +++ b/lib/classes/patchthesaurus/200.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection; class patchthesaurus_200 implements patchthesaurus_interface { diff --git a/lib/classes/patchthesaurus/201.php b/lib/classes/patchthesaurus/201.php index 33d73c8b7e..95eb641d1d 100644 --- a/lib/classes/patchthesaurus/201.php +++ b/lib/classes/patchthesaurus/201.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection; class patchthesaurus_201 implements patchthesaurus_interface { diff --git a/lib/classes/patchthesaurus/202.php b/lib/classes/patchthesaurus/202.php index 61d1d89803..50916c81c6 100644 --- a/lib/classes/patchthesaurus/202.php +++ b/lib/classes/patchthesaurus/202.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection; class patchthesaurus_202 implements patchthesaurus_interface { diff --git a/lib/classes/patchthesaurus/203.php b/lib/classes/patchthesaurus/203.php index 19b6573933..6d67ce10f2 100644 --- a/lib/classes/patchthesaurus/203.php +++ b/lib/classes/patchthesaurus/203.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection; class patchthesaurus_203 implements patchthesaurus_interface { diff --git a/lib/classes/patchthesaurus/204.php b/lib/classes/patchthesaurus/204.php index d512468d6d..b0a6d547f2 100644 --- a/lib/classes/patchthesaurus/204.php +++ b/lib/classes/patchthesaurus/204.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection; class patchthesaurus_204 implements patchthesaurus_interface { diff --git a/lib/classes/patchthesaurus/interface.php b/lib/classes/patchthesaurus/interface.php index e332c477cb..d642861151 100644 --- a/lib/classes/patchthesaurus/interface.php +++ b/lib/classes/patchthesaurus/interface.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Connection; interface patchthesaurus_interface { diff --git a/tests/Alchemy/Tests/Phrasea/Command/Setup/InstallTest.php b/tests/Alchemy/Tests/Phrasea/Command/Setup/InstallTest.php index 8229480097..acd1900e56 100644 --- a/tests/Alchemy/Tests/Phrasea/Command/Setup/InstallTest.php +++ b/tests/Alchemy/Tests/Phrasea/Command/Setup/InstallTest.php @@ -84,7 +84,7 @@ class InstallTest extends \PhraseanetTestCase self::$DI['cli']['phraseanet.installer']->expects($this->once()) ->method('install') - ->with($email, $password, $this->isInstanceOf('Doctrine\DBAL\Connection'), $serverName, $dataPath, $this->isInstanceOf('Doctrine\DBAL\Connection'), $template, $this->anything()); + ->with($email, $password, $this->isInstanceOf('Doctrine\DBAL\Driver\Connection'), $serverName, $dataPath, $this->isInstanceOf('Doctrine\DBAL\Driver\Connection'), $template, $this->anything()); $command = new Install('system:check'); $command->setHelperSet($helperSet); diff --git a/tests/Alchemy/Tests/Phrasea/Core/Connection/ConnectionProviderTest.php b/tests/Alchemy/Tests/Phrasea/Core/Connection/ConnectionProviderTest.php new file mode 100644 index 0000000000..73634f5e89 --- /dev/null +++ b/tests/Alchemy/Tests/Phrasea/Core/Connection/ConnectionProviderTest.php @@ -0,0 +1,17 @@ +get(self::$DI['app']['conf']->get(['main', 'database'])); + $conn->exec('SET @@local.wait_timeout= 1'); + usleep(1200000); + $conn->exec('SHOW DATABASES'); + } +} \ No newline at end of file diff --git a/tests/classes/collectionTest.php b/tests/classes/collectionTest.php index 6f7c084618..01bf5b2c22 100644 --- a/tests/classes/collectionTest.php +++ b/tests/classes/collectionTest.php @@ -112,7 +112,7 @@ class collectionTest extends \PhraseanetAuthenticatedTestCase public function testGet_connection() { - $this->assertInstanceOf('Doctrine\DBAL\Connection', self::$object->get_connection()); + $this->assertInstanceOf('Doctrine\DBAL\Driver\Connection', self::$object->get_connection()); } /** diff --git a/tests/classes/databox/fieldTest.php b/tests/classes/databox/fieldTest.php index 09f8927f10..57aaf4294b 100644 --- a/tests/classes/databox/fieldTest.php +++ b/tests/classes/databox/fieldTest.php @@ -70,8 +70,8 @@ class databox_fieldTest extends \PhraseanetTestCase public function testGet_connection() { - $this->assertInstanceOf('Doctrine\DBAL\Connection', $this->object_mono->get_connection()); - $this->assertInstanceOf('Doctrine\DBAL\Connection', $this->object_multi->get_connection()); + $this->assertInstanceOf('Doctrine\DBAL\Driver\Connection', $this->object_mono->get_connection()); + $this->assertInstanceOf('Doctrine\DBAL\Driver\Connection', $this->object_multi->get_connection()); } public function testGet_databox() From 6ba1e615468e26ca8380ed272f53b1f13694fe8b Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Mon, 3 Mar 2014 13:45:38 +0100 Subject: [PATCH 2/3] Reduce connection overhead --- lib/Alchemy/Phrasea/CLI.php | 6 +++++ .../Phrasea/Controller/Admin/Collection.php | 11 ++++++---- .../Connection/ReconnectableConnection.php | 13 +++++------ .../Core/PhraseaCLIExceptionHandler.php | 22 +++++++++++++++++++ www/index_dev.php | 2 ++ 5 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 lib/Alchemy/Phrasea/Core/PhraseaCLIExceptionHandler.php diff --git a/lib/Alchemy/Phrasea/CLI.php b/lib/Alchemy/Phrasea/CLI.php index 9698e890c7..9599db358d 100644 --- a/lib/Alchemy/Phrasea/CLI.php +++ b/lib/Alchemy/Phrasea/CLI.php @@ -14,6 +14,7 @@ namespace Alchemy\Phrasea; use Alchemy\Phrasea\Command\CommandInterface; use Alchemy\Phrasea\Core\CLIProvider\TranslationExtractorServiceProvider; use Alchemy\Phrasea\Core\CLIProvider\WebsocketServerServiceProvider; +use Alchemy\Phrasea\Core\PhraseaCLIExceptionHandler; use Alchemy\Phrasea\Exception\RuntimeException; use Symfony\Component\Console; use Alchemy\Phrasea\Core\CLIProvider\CLIDriversServiceProvider; @@ -23,6 +24,7 @@ use Alchemy\Phrasea\Core\CLIProvider\LessBuilderServiceProvider; use Alchemy\Phrasea\Core\CLIProvider\PluginServiceProvider; use Alchemy\Phrasea\Core\CLIProvider\SignalHandlerServiceProvider; use Alchemy\Phrasea\Core\CLIProvider\TaskManagerServiceProvider; +use Symfony\Component\Debug\ErrorHandler; /** * Phraseanet Command Line Application @@ -66,6 +68,10 @@ class CLI extends Application $this->register(new DoctrineMigrationServiceProvider()); $this->bindRoutes(); + + error_reporting(-1); + ErrorHandler::register(); + PhraseaCLIExceptionHandler::register(); } /** diff --git a/lib/Alchemy/Phrasea/Controller/Admin/Collection.php b/lib/Alchemy/Phrasea/Controller/Admin/Collection.php index 09469e9045..11ee0b31b8 100644 --- a/lib/Alchemy/Phrasea/Controller/Admin/Collection.php +++ b/lib/Alchemy/Phrasea/Controller/Admin/Collection.php @@ -955,12 +955,15 @@ class Collection implements ControllerProviderInterface $success = false; $collection = \collection::get_from_base_id($app, $bas_id); + $prefs = $request->request->get('str'); try { - $domdoc = new \DOMDocument(); - if ($domdoc->loadXML($request->request->get('str'))) { - $collection->set_prefs($domdoc); - $success = true; + if ('' !== trim($prefs)) { + $domdoc = new \DOMDocument(); + if (true === $domdoc->loadXML($prefs)) { + $collection->set_prefs($domdoc); + $success = true; + } } } catch (\Exception $e) { diff --git a/lib/Alchemy/Phrasea/Core/Connection/ReconnectableConnection.php b/lib/Alchemy/Phrasea/Core/Connection/ReconnectableConnection.php index 185a3ad900..f1798856c8 100644 --- a/lib/Alchemy/Phrasea/Core/Connection/ReconnectableConnection.php +++ b/lib/Alchemy/Phrasea/Core/Connection/ReconnectableConnection.php @@ -116,13 +116,8 @@ class ReconnectableConnection implements ConnectionInterface private function tryMethod($method, $args) { try { - set_error_handler(function ($errno, $errstr) { throw new \Exception($errstr, $errno); }); - $ret = call_user_func_array([$this->connection, $method], $args); - restore_error_handler(); - - return $ret; + return call_user_func_array([$this->connection, $method], $args); } catch (\Exception $exception) { - restore_error_handler(); $e = $exception; while ($e->getPrevious() && !$e instanceof \PDOException) { $e = $e->getPrevious(); @@ -133,7 +128,11 @@ class ReconnectableConnection implements ConnectionInterface return call_user_func_array([$this->connection, $method], $args); } - if ((false !== strpos($exception->getMessage(), 'MySQL server has gone away')) || (false !== strpos($exception->getMessage(), 'errno=32 Broken pipe'))) { + if ( + (false !== strpos($exception->getMessage(), 'MySQL server has gone away')) + || (false !== strpos($exception->getMessage(), 'Error while sending QUERY packet')) + || (false !== strpos($exception->getMessage(), 'errno=32 Broken pipe')) + ) { $this->connection->close(); $this->connection->connect(); diff --git a/lib/Alchemy/Phrasea/Core/PhraseaCLIExceptionHandler.php b/lib/Alchemy/Phrasea/Core/PhraseaCLIExceptionHandler.php new file mode 100644 index 0000000000..83a10f9c52 --- /dev/null +++ b/lib/Alchemy/Phrasea/Core/PhraseaCLIExceptionHandler.php @@ -0,0 +1,22 @@ + Date: Mon, 3 Mar 2014 14:09:13 +0100 Subject: [PATCH 3/3] Add logger to reconnectable connection --- .../Phrasea/Core/Connection/ConnectionProvider.php | 7 +++++-- .../Phrasea/Core/Connection/ReconnectableConnection.php | 8 +++++++- lib/Alchemy/Phrasea/Core/Provider/ORMServiceProvider.php | 2 +- .../Phrasea/Core/Connection/ConnectionProviderTest.php | 2 +- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/Alchemy/Phrasea/Core/Connection/ConnectionProvider.php b/lib/Alchemy/Phrasea/Core/Connection/ConnectionProvider.php index 07000ea6f2..d32d92922b 100644 --- a/lib/Alchemy/Phrasea/Core/Connection/ConnectionProvider.php +++ b/lib/Alchemy/Phrasea/Core/Connection/ConnectionProvider.php @@ -15,6 +15,7 @@ use Doctrine\Common\EventManager; use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\DriverManager; +use Psr\Log\LoggerInterface; class ConnectionProvider { @@ -24,9 +25,11 @@ class ConnectionProvider */ private $connections = []; private $eventManager; + private $logger; - public function __construct(Configuration $config, EventManager $eventManager) + public function __construct(Configuration $config, EventManager $eventManager, LoggerInterface $logger) { + $this->logger = $logger; $this->config = $config; $this->eventManager = $eventManager; } @@ -58,6 +61,6 @@ class ConnectionProvider return $this->connections[$key]; } - return $this->connections[$key] = new ReconnectableConnection(DriverManager::getConnection($params, $this->config, $this->eventManager)); + return $this->connections[$key] = new ReconnectableConnection(DriverManager::getConnection($params, $this->config, $this->eventManager), $this->logger); } } diff --git a/lib/Alchemy/Phrasea/Core/Connection/ReconnectableConnection.php b/lib/Alchemy/Phrasea/Core/Connection/ReconnectableConnection.php index f1798856c8..3ad3ca25d4 100644 --- a/lib/Alchemy/Phrasea/Core/Connection/ReconnectableConnection.php +++ b/lib/Alchemy/Phrasea/Core/Connection/ReconnectableConnection.php @@ -12,6 +12,7 @@ namespace Alchemy\Phrasea\Core\Connection; use Doctrine\DBAL\Driver\Connection as ConnectionInterface; +use Psr\Log\LoggerInterface; class ReconnectableConnection implements ConnectionInterface { @@ -19,10 +20,12 @@ class ReconnectableConnection implements ConnectionInterface /** @var Connection */ private $connection; + private $logger; - public function __construct(ConnectionInterface $connection) + public function __construct(ConnectionInterface $connection, LoggerInterface $logger) { $this->connection = $connection; + $this->logger = $logger; } /** @@ -125,6 +128,7 @@ class ReconnectableConnection implements ConnectionInterface if ($e instanceof \PDOException && $e->errorInfo[1] == self::MYSQL_CONNECTION_TIMED_WAIT_CODE) { $this->connection->close(); $this->connection->connect(); + $this->logger->notice('Connection to MySQL lost, reconnect okay.'); return call_user_func_array([$this->connection, $method], $args); } @@ -135,9 +139,11 @@ class ReconnectableConnection implements ConnectionInterface ) { $this->connection->close(); $this->connection->connect(); + $this->logger->notice('Connection to MySQL lost, reconnect okay.'); return call_user_func_array([$this->connection, $method], $args); } + $this->logger->critical('Connection to MySQL lost, unable to reconnect.', ['exception' => $exception]); throw $e; } diff --git a/lib/Alchemy/Phrasea/Core/Provider/ORMServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/ORMServiceProvider.php index 0985d294de..1eba97d48c 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/ORMServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/ORMServiceProvider.php @@ -150,7 +150,7 @@ class ORMServiceProvider implements ServiceProviderInterface }); $app['dbal.provider'] = $app->share(function (Application $app) { - return new ConnectionProvider($app['EM.config'], $app['EM.events-manager']); + return new ConnectionProvider($app['EM.config'], $app['EM.events-manager'], isset($app['task-manager.logger']) ? $app['task-manager.logger'] : $app['monolog']); }); $app['EM'] = $app->share(function (Application $app) { diff --git a/tests/Alchemy/Tests/Phrasea/Core/Connection/ConnectionProviderTest.php b/tests/Alchemy/Tests/Phrasea/Core/Connection/ConnectionProviderTest.php index 73634f5e89..0581adc449 100644 --- a/tests/Alchemy/Tests/Phrasea/Core/Connection/ConnectionProviderTest.php +++ b/tests/Alchemy/Tests/Phrasea/Core/Connection/ConnectionProviderTest.php @@ -8,7 +8,7 @@ class ConnectionProviderTest extends \PhraseanetTestCase { public function testMysqlTimeoutIsHandled() { - $provider = new ConnectionProvider(self::$DI['app']['EM.config'], self::$DI['app']['EM.events-manager']); + $provider = new ConnectionProvider(self::$DI['app']['EM.config'], self::$DI['app']['EM.events-manager'], $this->createLoggerMock()); $conn = $provider->get(self::$DI['app']['conf']->get(['main', 'database'])); $conn->exec('SET @@local.wait_timeout= 1'); usleep(1200000);