Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possible mem leak in Query::getSingleScalarResult #7649

Closed
flaushi opened this issue Mar 20, 2019 · 15 comments
Closed

Possible mem leak in Query::getSingleScalarResult #7649

flaushi opened this issue Mar 20, 2019 · 15 comments
Assignees

Comments

@flaushi
Copy link
Contributor

flaushi commented Mar 20, 2019

Bug Report

My DQL query selects a number from an entity:

$query = $this->_em->createQuery("
            SELECT s.value AS value
            FROM  App\Entity\DataSample s 
            WHERE ...

");
$query->setMaxResults(1);

This query is executed tens of thousands of times inside a Symfony console application.
Now if I use getSingleScalarResult the memory consumption will explode.

try {
            $r = $query->getSingleScalarResult();
        } catch (NoResultException $e) {
            unset($e);
            $r = '0.0';
        }
        unset($query);
        return $r;

However, switching to getResult helps:

$r = $query->getResult();

        if(empty($r)){
           $r = '0.0';
        } else
            $r = $r[0]['value'];
        return $r;

In the latter case, memory consumption stays bearable.

I found this issue and developed a TestCase.

<?php

declare(strict_types=1);

namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\Annotation as ORM;
use Doctrine\ORM\NoResultException;
use Doctrine\Tests\OrmFunctionalTestCase;

/**
 * @group GH7642
 */
class GH7642Test extends OrmFunctionalTestCase
{

    private $limit = 25000;
    private $numIterations = 10000;
    /**
     * {@inheritDoc}
     */
    protected function setUp() : void
    {
        parent::setUp();

        $this->schemaTool->createSchema(
            [
                $this->em->getClassMetadata(GH7642Entity::class),
            ]
        );

    }

    private function queryResult() {
        $query = $this->em->createQuery(<<<DQL
            SELECT e.points AS value FROM Doctrine\Tests\ORM\Functional\Ticket\GH7642Entity e
DQL
        );
        $query->setMaxResults(1);
        $queryResult = $query->getResult();
        if(empty($queryResult))
            return 0;
        else
            return $queryResult[0]['value'];
    }

    private function standardGetResultCase() : float {

        // iterate over results
        for ($i = 0 ; $i < $this->numIterations ; $i++){
            $this->queryResult();
        }
        return memory_get_peak_usage(true) / 1000000; // in MB

    }

    private function queryWithSingleScalarResult()  {
        $query = $this->em->createQuery(<<<DQL
            SELECT e.points AS value FROM Doctrine\Tests\ORM\Functional\Ticket\GH7642Entity e
DQL
    );
        $query->setMaxResults(1);
        try {
            $r = $query->getSingleScalarResult();
        } catch (NoResultException $e){
            $r = 0;
        }
        return $r;
    }

    private function singleScalarCase() : float {

        // iterate over results
        for ($i = 0 ; $i < $this->numIterations ; $i++){
            $this->queryWithSingleScalarResult();
        }
        return memory_get_peak_usage(true) / 1000000; // in MB

    }

    public function testIssue() : void
    {

        for($i=0;$i<$this->limit;$i++) {
            $entity = new GH7642Entity(mt_rand(0,100));

            $this->em->persist($entity);
        }

        $this->em->flush();
        $this->em->clear();
        gc_collect_cycles();

        $rawQueryMemory = $this->standardGetResultCase();

        // clear all
        $this->em->flush();
        $this->em->clear();
        gc_collect_cycles();

        $namedQueryMemory = $this->singleScalarCase();

        var_dump([
            'standard'  =>$rawQueryMemory,
            'singleScalar' => $namedQueryMemory]);
        // lets assume that named query should use 2x more memory at most.
        $this->assertLessThanOrEqual($rawQueryMemory*2, $namedQueryMemory);

    }

}

/**
 * @ORM\Entity
 */
class GH7642Entity
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue
     */
    public $id;

    /**
     * @var int
     * @ORM\Column(type="decimal", precision=20, scale=4)
     */
    public $points;

    public function __construct($points)
    {
        $this->points = $points;
    }
}

Unfortunately the test case does not produce the memory leak.

However, when repeating the queries, memory consumption does increase. This should not be the case IMHO?

for ($i = 0 ; $i < 100 ; $i++) {
//            $rawQueryMemory = $this->standardGetResultCase();
// or:
            $rawQueryMemory = $this->singleScalarCase();
            var_dump($rawQueryMemory);
        }

gives

float(88.08448)
float(88.08448)
float(88.08448)
float(88.08448)
float(90.181632)
float(90.181632)
float(90.181632)
float(90.181632)
float(90.181632)
float(90.181632)
float(94.375936)
float(94.375936)
float(98.57024)
float(100.667392)
float(104.861696)
float(109.056)
float(113.250304)
float(117.444608)
float(119.54176)
float(123.736064)
float(127.930368)
float(132.124672)

Fatal error: Allowed memory size of 134217728 bytes exhausted 

One has to say that I had to switch to PHP 7.2 for the test case to compile and also pulled orm from master branch. My software uses PHP 7.1 and ORM 2.6.2

doctrine/annotations                 v1.6.0   MIT                
doctrine/cache                       v1.8.0   MIT                
doctrine/collections                 v1.5.0   MIT                
doctrine/common                      v2.10.0  MIT                
doctrine/data-fixtures               v1.3.1   MIT                
doctrine/dbal                        v2.9.2   MIT                
doctrine/doctrine-bundle             1.10.1   MIT                
doctrine/doctrine-cache-bundle       1.3.5    MIT                
doctrine/doctrine-fixtures-bundle    3.1.0    MIT                
doctrine/doctrine-migrations-bundle  v1.3.2   MIT                
doctrine/event-manager               v1.0.0   MIT                
doctrine/inflector                   v1.3.0   MIT                
doctrine/instantiator                1.1.0    MIT                
doctrine/lexer                       v1.0.1   MIT                
doctrine/migrations                  v1.8.1   MIT                
doctrine/orm                         v2.6.2   MIT                
doctrine/persistence                 v1.1.0   MIT                
doctrine/reflection                  v1.0.0   MIT   
@Ocramius
Copy link
Member

Is the memory usage progression coming from a symfony context? Can it be reproduced without symfony (since the test case seems to deny that)?

Maybe this is related to query logging?

@flaushi
Copy link
Contributor Author

flaushi commented Mar 20, 2019

As I pointed out, in my fuctional test case, memory does explode, too. However there it does not make a difference if I use getResult or getSingleScalarResult.

In my production code, I am only exchanging getSingleScalarResult with getResult in the repository code. In first case, 1G of memory is exhausted after 1700 iterations. With getResult, all 17000 iterations run with peak mem usage of 480 MB.

@flaushi
Copy link
Contributor Author

flaushi commented Mar 20, 2019

https://gist.github.com/flaushi/03b18152dea60bc2dba1ff405140b820
Run this test case with

php -d memory_limit=1G vendor/bin/phpunit tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php

gives:

$ php -d memory_limit=1G vendor/bin/phpunit tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php
PHPUnit 6.5.13 by Sebastian Bergmann and contributors.

Runtime:       PHP 7.1.24
Configuration: /Users/oliverdemetz/git/orm/phpunit.xml.dist

PHP Fatal error:  Allowed memory size of 1073741824 bytes exhausted (tried to allocate 20480 bytes) in /Users/oliverdemetz/git/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php on line 548
float(123.736064)
float(123.736064)
float(123.736064)
float(123.736064)
float(125.833216)
float(125.833216)
float(125.833216)
float(125.833216)
float(125.833216)
float(125.833216)
float(130.02752)
float(130.02752)
float(130.02752)
float(130.02752)
float(130.02752)
float(134.221824)
float(138.416128)
float(142.610432)
float(144.707584)
float(148.901888)
float(153.096192)
float(157.290496)
float(161.4848)
float(171.97056)
float(176.164864)
float(180.359168)
float(184.553472)
float(188.747776)
float(190.844928)
float(195.039232)
float(199.233536)
float(203.42784)
float(207.622144)
float(209.719296)
float(213.9136)
float(218.107904)
float(222.302208)
float(226.496512)
float(228.593664)
float(232.787968)
float(236.982272)
float(241.176576)
float(245.37088)
float(247.468032)
float(251.662336)
float(255.85664)
float(260.050944)
float(264.245248)
float(266.3424)
float(287.31392)
float(291.508224)
float(295.702528)
float(299.896832)
float(301.993984)
float(306.188288)
float(310.382592)
float(314.576896)
float(316.674048)
float(320.868352)
float(325.062656)
float(329.25696)
float(333.451264)
float(335.548416)
float(339.74272)
float(343.937024)
float(348.131328)
float(352.325632)
float(354.422784)
float(358.617088)
float(362.811392)
float(367.005696)
float(371.2)
float(373.297152)
float(377.491456)
float(381.68576)
float(385.880064)
float(390.074368)
float(392.17152)
float(396.365824)
float(400.560128)
float(404.754432)
float(408.948736)
float(411.045888)
float(415.240192)
float(419.434496)
float(423.6288)
float(427.823104)
float(429.920256)
float(434.11456)
float(438.308864)
float(442.503168)
float(446.697472)
float(448.794624)
float(452.988928)
float(457.183232)
float(461.377536)
float(465.57184)
float(467.668992)
float(471.863296)
float(476.0576)
float(480.251904)
float(484.446208)
float(520.097792)
float(524.292096)
float(528.4864)
float(532.680704)
float(534.777856)
float(538.97216)
float(543.166464)
float(547.360768)
float(551.555072)
float(553.652224)
float(557.846528)
float(562.040832)
float(566.235136)
float(570.42944)
float(572.526592)
float(576.720896)
float(580.9152)
float(585.109504)
float(589.303808)
float(591.40096)
float(595.595264)
float(599.789568)
float(603.983872)
float(608.178176)
float(610.275328)
float(614.469632)
float(618.663936)
float(622.85824)
float(627.052544)
float(629.149696)
float(633.344)
float(637.538304)
float(641.732608)
float(645.926912)
float(648.024064)
float(652.218368)
float(656.412672)
float(660.606976)
float(664.80128)
float(666.898432)
float(671.092736)
float(675.28704)
float(679.481344)
float(683.675648)
float(685.7728)
float(689.967104)
float(694.161408)
float(698.355712)
float(702.550016)
float(704.647168)
float(708.841472)
float(713.035776)
float(717.23008)
float(721.424384)
float(723.521536)
float(727.71584)
float(731.910144)
float(736.104448)
float(740.298752)
float(742.395904)
float(746.590208)
float(750.784512)
float(754.978816)
float(757.075968)
float(761.270272)
float(765.464576)
float(769.65888)
float(773.853184)
float(775.950336)
float(780.14464)
float(784.338944)
float(788.533248)
float(792.727552)
float(794.824704)
float(799.019008)
float(803.213312)
float(807.407616)
float(811.60192)
float(813.699072)
float(817.893376)
float(822.08768)
float(826.281984)
float(830.476288)
float(832.57344)
float(836.767744)
float(840.962048)
float(845.156352)
float(849.350656)
float(851.447808)
float(855.642112)
float(859.836416)
float(864.03072)
float(868.225024)
float(870.322176)
float(874.51648)
float(878.710784)
float(882.905088)
float(887.099392)
float(889.196544)
float(893.390848)
float(897.585152)
float(901.779456)
float(905.97376)
float(908.070912)
float(912.265216)
float(983.568384)
float(987.762688)
float(991.956992)
float(994.054144)
float(998.248448)
float(1002.442752)
float(1006.637056)
float(1010.83136)
float(1012.928512)
float(1017.122816)
float(1021.31712)
float(1025.511424)
float(1029.705728)
float(1031.80288)
float(1035.997184)
float(1040.191488)
float(1044.385792)
float(1048.580096)
float(1050.677248)
float(1054.871552)
float(1059.065856)
float(1063.26016)
float(1065.357312)
float(1069.551616)
float(1071.648768)
float(1071.648768)
float(1071.648768)
float(1071.648768)
float(1071.648768)
float(1071.648768)

Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to alloca...

@Ocramius
Copy link
Member

I'm referring to this bit:

Unfortunately the test case does not produce the memory leak.

Can the test be adapted to reproduce the problem?

@Ocramius
Copy link
Member

Oh, posted at the same time. Lemme check that test case then 👍

@Ocramius
Copy link
Member

I just went through this, and found out that the OrmFunctionalTestCase already leaks (due to static connection and SQL logger).

This is the rewritten test, which in my case doesn't leak:

<?php

declare(strict_types=1);

namespace Doctrine\Tests\ORM\Functional\Ticket;

use Doctrine\Common\Cache\ArrayCache;
use Doctrine\DBAL\Driver\PDOSqlite\Driver;
use Doctrine\DBAL\DriverManager;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\Tools\SchemaTool;
use PHPUnit\Framework\TestCase;

/**
 * git checkout 2.6
 * run with:
 * php -d memory_limit=1G vendor/bin/phpunit tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php
 */
class GH7642Test extends TestCase
{
    private $limit = 250;

    private $numIterations = 100;

    /** @var int */
    private $initialMemoryUsage = 0;

    /** @var EntityManagerInterface */
    private $entityManager;

    /**
     * {@inheritDoc}
     */
    protected function setUp() : void
    {
        parent::setUp();

        $config = new Configuration();
        $config->setMetadataCacheImpl(new ArrayCache());
        $config->setQueryCacheImpl(new ArrayCache());
        $config->setProxyDir(__DIR__ . '/../../../Proxies');
        $config->setProxyNamespace('Doctrine\Tests\Proxies');
        $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver(__FILE__));

        $connection = DriverManager::getConnection(
            [
                'driverClass' => Driver::class,
                'memory'      => true,
            ],
            $config
        );

        $this->entityManager = EntityManager::create($connection, $config);

        (new SchemaTool($this->entityManager))->createSchema([
            $this->entityManager->getClassMetadata(GH7642Entity::class),
        ]);

        gc_collect_cycles();
        $this->initialMemoryUsage = memory_get_usage();
    }

    private function queryResult()
    {
        $query = $this->entityManager->createQuery(<<<DQL
            SELECT e.points AS value FROM Doctrine\Tests\ORM\Functional\Ticket\GH7642Entity e
DQL
        );
        $query->setMaxResults(1);
        $queryResult = $query->getResult();
        if (empty($queryResult)) {
            return 0;
        } else {
            return $queryResult[0]['value'];
        }
    }

    private function standardGetResultCase() : int
    {
        // iterate over results
        for ($i = 0; $i < $this->numIterations; $i++) {
            $this->queryResult();
        }
        gc_collect_cycles();
        return memory_get_usage();
    }

    private function queryWithSingleScalarResult()
    {
        $query = $this->entityManager->createQuery(<<<DQL
            SELECT e.points AS value FROM Doctrine\Tests\ORM\Functional\Ticket\GH7642Entity e
DQL
        );
        $query->setMaxResults(1);
        try {
            $r = $query->getSingleScalarResult();
        } catch (NoResultException $e) {
            $r = 0;
        }
        return $r;
    }

    private function singleScalarCase() : int
    {
        // iterate over results
        for ($i = 0; $i < $this->numIterations; $i++) {
            $this->queryWithSingleScalarResult();
        }
        return memory_get_usage();
    }

    public function testIssue() : void
    {
        for ($i = 0; $i < $this->limit; $i++) {
            $entity = new GH7642Entity(mt_rand(0, 100));
            $this->entityManager->persist($entity);
        }
        $this->entityManager->flush();
        $this->entityManager->clear();
        gc_collect_cycles();
        for ($i = 0; $i < $this->numIterations; $i++) {
            $rawQueryMemory = $this->standardGetResultCase();
//            $rawQueryMemory = $this->singleScalarCase();
            var_dump($rawQueryMemory - $this->initialMemoryUsage);
        }
        // clear all
        $this->entityManager->flush();
        $this->entityManager->clear();
        gc_collect_cycles();
        $namedQueryMemory = $this->singleScalarCase();
        var_dump([
            'standard'     => $rawQueryMemory - $this->initialMemoryUsage,
            'singleScalar' => $namedQueryMemory - $this->initialMemoryUsage]);
        // lets assume that named query should use 2x more memory at most.
        $this->assertLessThanOrEqual($rawQueryMemory * 2, $namedQueryMemory);
    }
}

/**
 * @Entity
 */
class GH7642Entity
{
    /**
     * @Id
     * @Column(type="integer")
     * @GeneratedValue
     */
    public $id;

    /**
     * @var int
     * @Column(type="decimal", precision=20, scale=4)
     */
    public $points;

    public function __construct($points)
    {
        $this->points = $points;
    }
}

Produces:

/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:137:
array(2) {
  'standard' =>
  int(2212880)
  'singleScalar' =>
  int(2221776)
}

Note how I use memory_get_usage(), not memory_get_peak_usage(), since PHPUnit also adds overhead.

Can you check if the modified test is flawed or not?

@flaushi
Copy link
Contributor Author

flaushi commented Mar 20, 2019

I can confirm that the modified test does not leak memory.
Still, I definitely see the described behavior in my console application (no debug, no sqlLogger and no second level cache logger).
Definitely, the one and only difference is my repository method:

       if($this->_em->getConfiguration()->getSQLLogger() !== null)
            throw new \Exception('SQL Logger is non-zero');

        if($this->_em->getConfiguration()->getSecondLevelCacheConfiguration()->getCacheLogger() !== null)
            throw new \Exception('SLC Logger is non-zero');

        /**
         * This code does not leak:
         */
        /*
        $r = $query->getResult();
        if(empty($r)){
            if($w->intervalConfined)
                $r = '0.0';
            else
                $r = 'no value';
        } else
            $r = $r[0]['value'];
        return $r;
        */
        /**
         * This code does leak:
         */
        try {
            $r = $query->getSingleScalarResult();
        } catch (NoResultException $e) {
            unset($e);
            if($w->intervalConfined)
                $r = '0.0';
            else
                $r = 'no value';
        }
        unset($query);
        return $r;

@flaushi
Copy link
Contributor Author

flaushi commented Mar 20, 2019

I also wrapped the line

$r = $query->getResult();

in a try-catch to check if this produces the leak, but no.

@Ocramius
Copy link
Member

Since we can exclude ORM from being the root cause here, I think you should investigate and see what the ORM configuration (EntityManager#getConfiguration() and Connection#getConfiguration()) yield. Possibly a framework dependency collecting things over time.

@flaushi
Copy link
Contributor Author

flaushi commented Mar 20, 2019

Yeah I also thought that's it but couldn't find anything.

I am irritated about the fact that getSingleScalarResult is the reproducible trigger...

@Ocramius
Copy link
Member

Maybe this is related to some listeners?

@flaushi
Copy link
Contributor Author

flaushi commented Mar 20, 2019

I do have listeners, but only pre/posPersist/Update/Flush. This command does not issue any $em->flush() or persist so none of them should be called.
I'll try a MySQL connection now...

@flaushi
Copy link
Contributor Author

flaushi commented Mar 20, 2019

I can't believe it. It seems to be the PostgreSQL connection.
This explains why the TestCase did work: it's a SQLite in-memory db.

I uploaded a 100 % equal data set to a Postgres and MySQL connection.
On MySQL the memory consumption is the same for both getResult and getSingleScalarResult,

on Postgresql the getSingleScalarResult version has increasing memory consumption while the getResult is clean. Incredible.

@lcobucci
Copy link
Member

lcobucci commented Oct 2, 2019

@flaushi @Ocramius shall we close this one?

@flaushi
Copy link
Contributor Author

flaushi commented Oct 2, 2019

Yes, I did not re-encounter the problem since then. Thanks for your support!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants