Recently, I try to create a complete build archive of the Sonata Project web site (including vendor and cache) from a remote machine to deploy quickly on the production server.
I get a few problems and one is quite interesting as it show how fine tuning of the Symfony2 and the Doctrine configuration is important.
To generate the cache, I just start the cache:warmup
command with the production settings and I get a nice exception:
[Doctrine\DBAL\Driver\PDOException]
SQLSTATE[HY000] [1130] Host 'xxxxxx' is not allowed to connect to this MySQL server
This is quite weird to open a connection to the MySQL server to build the cache. So I restart the command with verbosity enabled, and get a nice stack trace. You need to read it bottom to top:
() at ROOT/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:43
PDO->__construct() at ROOT/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:43
Doctrine\DBAL\Driver\PDOConnection->__construct() at ROOT/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php:45
Doctrine\DBAL\Driver\PDOMySql\Driver->connect() at ROOT/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php:360
Doctrine\DBAL\Connection->connect() at ROOT/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php:429
Doctrine\DBAL\Connection->getDatabasePlatformVersion() at ROOT/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php:389
Doctrine\DBAL\Connection->detectDatabasePlatform() at ROOT/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php:328
Doctrine\DBAL\Connection->getDatabasePlatform() at ROOT/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php:81
Doctrine\ORM\Mapping\ClassMetadataFactory->initialize() at ROOT/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php:292
Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory->loadMetadata() at ROOT/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php:211
Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory->getMetadataFor() at ROOT/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:265
Doctrine\ORM\EntityManager->getClassMetadata() at ROOT/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php:67
Doctrine\ORM\Repository\DefaultRepositoryFactory->createRepository() at ROOT/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php:50
Doctrine\ORM\Repository\DefaultRepositoryFactory->getRepository() at ROOT/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:665
Doctrine\ORM\EntityManager->getRepository() at ROOT/vendor/friendsofsymfony/user-bundle/FOS/UserBundle/Doctrine/UserManager.php:40
FOS\UserBundle\Doctrine\UserManager->__construct() at ROOT/app/cache/dev/appDevDebugProjectContainer.php:2209
The first understanding is that the ClassMetadataFactory
from Doctrine requires some information from the database.
After digging into the code, I found out that Doctrine need to know the target plateform.
So it connect to the database to found out which version of MySQL, or others databases you are using.
The stack trace is not complete, here another fragment:
FOS\UserBundle\Doctrine\UserManager->__construct() at ROOT/app/cache/dev/appDevDebugProjectContainer.php:2209
appDevDebugProjectContainer->getFosUser_UserManagerService() at ROOT/app/bootstrap.php.cache:2097
Symfony\Component\DependencyInjection\Container->get() at ROOT/app/cache/dev/appDevDebugProjectContainer.php:11108
appDevDebugProjectContainer->getSecurity_Authentication_ManagerService() at ROOT/app/bootstrap.php.cache:2097
Symfony\Component\DependencyInjection\Container->get() at ROOT/app/cache/dev/appDevDebugProjectContainer.php:3306
appDevDebugProjectContainer->getSecurity_AuthorizationCheckerService() at ROOT/app/bootstrap.php.cache:2097
Symfony\Component\DependencyInjection\Container->get() at ROOT/app/cache/dev/appDevDebugProjectContainer.php:3319
appDevDebugProjectContainer->getSecurity_ContextService() at ROOT/app/bootstrap.php.cache:2097
Symfony\Component\DependencyInjection\Container->get() at ROOT/app/cache/dev/appDevDebugProjectContainer.php:10607
appDevDebugProjectContainer->getTwigService() at ROOT/app/bootstrap.php.cache:2097
As you can see, anytime you want the twig
service you are opening a database connection to know the target plateform. The
target plateform will rarely change over time …
There is an hidden option in Symfony2 where you can configure the plateform service to use for a specific connection.
services:
doctrine.plateform.mysql:
class: Doctrine\DBAL\Platforms\MySqlPlatform
doctrine:
dbal:
default_connection: default
connections:
default:
driver: %database_driver%
dbname: %database_name%
user: %database_user%
host: %database_host%
password: %database_password%
platform_service: doctrine.plateform.mysql
The doctrine.plateform.mysql
is the service identifier of the plateform used in the platform_service
option
from the connection settings. The complete plateforms list is available on the doctrine project’s website:
http://doctrine-dbal.readthedocs.org/en/latest/reference/platforms.html
You should really tweak this settings if you have some actions or background processes which do not require any database connections.