Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHPUnit tests and Doctrine, too many connections

I'm facing problems with "too many connections" for PHPUnit tests for ZF3 and Doctrine, because I'm executing ~200 tests per PHPUnit execution. I've already found some questions and answers on stack overflow but non of these work.

My setup: ZF2/ZF3, Doctrine 2 and PHPUnit.

I have a base test class for all tests and the setUp and tearDown function look like this:

public function setUp()
{
    $this->setApplicationConfig(Bootstrap::getConfig());
    Bootstrap::loadAllFixtures();
    if (!static::$em) {
        echo "init em";
        static::$em = Bootstrap::getEntityManager();
    }
    parent::setUp();
    ....
}

public function tearDown()
{
    parent::tearDown();
    static::$em->flush();
    static::$em->clear();
    static::$em->getConnection()->close();
    $refl = new \ReflectionObject($this);
    foreach ($refl->getProperties() as $prop) {
        if (!$prop->isStatic() && 0 !== strpos($prop->getDeclaringClass()->getName(), 'PHPUnit_')) {
            $prop->setAccessible(true);
            $prop->setValue($this, null);
        }
    }
    gc_collect_cycles();
}

public static function (Bootstrap::)loadAllFixtures()
{
    static::$em->getConnection()->executeUpdate("SET foreign_key_checks = 0;");
    $loader = new Loader();
    foreach (self::$config['data-fixture'] as $fixtureDir) {
        $loader->loadFromDirectory($fixtureDir);
    }
    $purger = new ORMPurger(static::$em);
    $executor = new ORMExecutor(static::$em, $purger);
    $executor->execute($loader->getFixtures());
    $executor = null;
    $purger = null;
    static::$em->getConnection()->executeUpdate("SET foreign_key_checks = 1;");
    static::$em->flush();
    static::$em->clear();
}

I'm monitoring my local MySQL server with innotop and the number of connections is increasing.

Do you have any ideas what I'm missing?

Thank you, Alexander

Update 14.02.2017: I've changed functions to use static::$em and added Bootstrap::loadAllFixtures method.

If I add static::$em->close() to tearDown method, all following test fail with message like "EntityManager already closed". echo "init em"; is only call once and shown for the first test. Is there a possibility to check if my Application opens connections without closing them? My test cases are based on AbstractHttpControllerTestCase

like image 520
Alexander Lampret Avatar asked Feb 12 '17 13:02

Alexander Lampret


1 Answers

I came across this problem too. Following the advice in PHPUnit's documentation I had done the following:

final public function getConnection()
{
    if ($this->conn === null) {
        if (self::$pdo == null) {

            //We get the EM from dependency injection container
            $container = $this->getContainer();
            self::$pdo = $container->get('Doctrine.EntityManager')->getConnection()->getWrappedConnection();
        }
        $this->conn = $this->createDefaultDBConnection(self::$pdo, 'spark_api_docker');
    }

    return $this->conn;
}

While self:$pdo was being shared, the number of 'threads_connected', when I observed show status like '%onn%'; on my database, crept up until it reached the limit.

I found two solutions to this:


1) Close the connection after each test

public function tearDown()
{
    parent::tearDown();

    //You'll probably need to get hold of our entity manager another way
    $this->getContainer()->get('Doctrine.EntityManager')->getConnection()->close();
}

importantly, do not set self::$pdo to null. I had seen this as a recommendation elsewhere, but there's no point setting it as a static property and then resetting it after each test.

This works my closing connections that are no longer needed. When a testcase is finished, unless you have closed the connection it will remain open until the script ends (i.e. PHPUnit finishes running your test). Since you're creating a new connection for each test case, the number of connections goes up.


2) Run each test in a seperate PHP thread

This is the sledgehammer approach. It will likely impact the speed of your tests to one degree or another. In your phpunit.xml`:

<?xml version="1.0" encoding="UTF-8"?>

<phpunit
    ...
    processIsolation = "true"
    >
    ...
</phpunit>

Returning to PHPUnit's advice, storing the connection and PDO helps with not creating new connections for each test but does not help you when you have many test cases. Each test case gets instantianted in the same thread, and each will create a new connection.

like image 173
Stephen Harris Avatar answered Nov 04 '22 04:11

Stephen Harris