8

I have a fixtures that loads a huge amount of data and all the time I run into this error:

Fatal error: Allowed memory size of 2147483648 bytes exhausted (tried to allocate 16777224 bytes) in /var/www/html/platform-cm/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php on line 65

[Symfony\Component\Debug\Exception\OutOfMemoryException] Error: Allowed memory size of 2147483648 bytes exhausted (tried to allocate 16777224 bytes)

After research a bit I found this post where I read that logging could be the cause of the issue because AppKernel is instantiated with debug set to true by default and then the SQL commands get stored in memory for each iteration.

The first attempt without disable the debug at AppKernel was run the command as:

doctrine:fixtures:load --no-debug

But I didn't get luck since the same error still.

The second attempt was disable the debug at config_dev.yml but this is not recommended since I am getting ride of every logs but didn't work neither.

monolog:
    handlers:
        main:
            type:   stream
            path:   "%kernel.logs_dir%/%kernel.environment%.log"
            level:  debug
#        console:
#            type:   console
#            bubble: false
#            verbosity_levels:
#                VERBOSITY_VERBOSE: INFO
#                VERBOSITY_VERY_VERBOSE: DEBUG
#            channels: ["!doctrine"]
#        console_very_verbose:
#            type:   console
#            bubble: false
#            verbosity_levels:
#                VERBOSITY_VERBOSE: NOTICE
#                VERBOSITY_VERY_VERBOSE: NOTICE
#                VERBOSITY_DEBUG: DEBUG
#            channels: ["doctrine"]

So, this is how my fixture looks like:

class LoadMsisdn extends AbstractFixture implements OrderedFixtureInterface
{
    public function getOrder()
    {
        return 13;
    }

    public function load(ObjectManager $manager)
    {
        $content = file_get_contents('number.txt');
        $numbers = explode(',', $content);
        shuffle($numbers);

        foreach ($numbers as $key => $number) {
            $msisdn = new Msisdn();
            $msisdn->setMsisdn($number);
            $msisdn->setBlocked((rand(1, 1000) % 10) < 7);
            $msisdn->setOperator($this->getReference('operator-' . rand(45, 47)));

            $this->addReference('msisdn-' . $key, $msisdn);
            $manager->persist($msisdn);
        }

        $manager->flush();
    }
}

How do I disable the logger if I need to do it from EntityManager as shown in a answer on the same post?

$em->getConnection()->getConfiguration()->setSQLLogger(null);
Community
  • 1
  • 1
ReynierPM
  • 17,594
  • 53
  • 193
  • 363
  • Maybe create your own command, which turns off logging and then runs the built-in fixtures command. Have a look at Doctrine/Bundle/FixturesBundle/Command/LoadDataFixturesDoctrineCommand.php. You could probably subclass that and change the command name, and add your logging line to execute(). – Ryan Feb 04 '16 at 06:12
  • The entity manager is being passed into the load method. Could you not use that? – qooplmao Feb 04 '16 at 09:13
  • Possible duplicate of [how to stop doctrine to log queries](http://stackoverflow.com/questions/28191382/how-to-stop-doctrine-to-log-queries) or [Memory leaks Symfony2 Doctrine2 / exceed memory limit](http://stackoverflow.com/questions/9699185/memory-leaks-symfony2-doctrine2-exceed-memory-limit) – Jakub Zalas Feb 04 '16 at 09:30
  • @qooplmao where is the `EntityManager` in the `load()` method? `$manager` is type of `Doctrine\Common\Persistence\ObjectManager` and that class doesn't has a way to set SQLLogger to null, could you explain your suggestion? – ReynierPM Feb 04 '16 at 13:04
  • @JakubZalas if you follow the post I linked to my answer you'll see is the same post you are suggesting as duplicate so, this is not a duplicate I am just asking because my environment where issue happen is complete different, could you point me on the right direction? – ReynierPM Feb 04 '16 at 13:10
  • @ReynierPM, as you can see here (https://github.com/doctrine/data-fixtures/blob/master/lib/Doctrine/Common/DataFixtures/Executor/ORMExecutor.php#L88) the entity manager is the object manager (providing you are using the ORM). `Doctrine\Common\Persistence\ObjectManager` is an interface that the entity manager implements. Did you try `$manager...->setSQLLogger(null)` on the manager? – qooplmao Feb 04 '16 at 13:16
  • How do you know it's the logger? Error comes from there, but something else might be leaking memory. You're not detaching entities from the manager, so they're all there in memory. – Jakub Zalas Feb 04 '16 at 13:18
  • @qooplmao yes, that method isn't part of `ObjectManager` so I end with this error: `Fatal error: Call to undefined method Doctrine\ORM\EntityManager::setSQLLogger()` – ReynierPM Feb 04 '16 at 13:19
  • @JakubZalas sorry my Symfony knowledge is not so good, I know how to detach the entities but how this would help me? I am running a fixtures load and this it's only creating 500~1000 records, what else could be the issue? – ReynierPM Feb 04 '16 at 13:20
  • 1
    The `...`'s were to shorten the statement. As you can now see, the manager is `Doctrine\ORM\EntityManager` meaning it is the entity manager (`$em` in your question) so you can use `$manager->getConnection()->getConfiguration()->setSQLLogger(null);` – qooplmao Feb 04 '16 at 13:21
  • @qooplmao ok, your latest suggestion did works. Thanks, pls post a answer so I can give you the points for your help – ReynierPM Feb 04 '16 at 13:44

2 Answers2

13

The object manager that is being passed into the load method is an instance of the the entity manager (Doctrine\Common\Persistence\ObjectManager is just an interface that the entity/document/etc managers implement).

This mean that you can use the same command as in your question to nullify the SQL logger like..

$manager->getConnection()->getConfiguration()->setSQLLogger(null);

One thing to note is that the default logging setting for a DBAL connection is %kernel.debug% meaning that, unless you have overridden it in your config, the logging should only happen in the dev environment. I can see you have tried using the --no-debug option but I can only assume that, as the logger is set during the container compilation, that it doesn't unset it to the container not being rebuilt.


Symfony 3/4/5 approach in test case

final class SomeTestCase extends KernelTestCase
{
    protected function setUp(): void
    {
        $this->bootKernel('...');

        // @see https://stackoverflow.com/a/35222045/1348344
        // disable Doctrine logs in tests output
        $entityManager = self::$container->get(EntityManagerInterface::class);
        $entityManager->getConfiguration();
        $connection = $entityManager->getConnection();

        /** @var Configuration $configuration */
        $configuration = $connection->getConfiguration();
        $configuration->setSQLLogger(null);
    }
}
Tomas Votruba
  • 23,240
  • 9
  • 79
  • 115
qooplmao
  • 17,622
  • 2
  • 44
  • 69
  • `setSqlLogger()` method is deprecated since Doctrine 3.2, see: https://github.com/doctrine/dbal/pull/4967 – long Oct 18 '22 at 13:02
8

I am not sure if it will help with the memory limit issue, but you can also try to change logging (more comfortably) via YAML configuration:


Disable Doctrine log channel

(as in your commented code)

monolog:
    handlers:
        main:
            type:   stream
            path:   "%kernel.logs_dir%/%kernel.environment%.log"
            level:  debug
            channels: ["!doctrine"]    # "!event", "!php"

or Setting a custom dummy logger

In services.yaml set the class for the default Doctrine logger:

doctrine.dbal.logger:
        class: App\ORM\Doctrine\DBAL\Logging\DummySQLLogger

And enabling it in doctrine.yaml:

doctrine:
    dbal:
        driver: 'pdo_mysql'
        logging: true

The custom logger must implement the interface Doctrine\DBAL\Logging\SQLLogger. The concrete methods startQuery and stopQuery can be left empty, to skip logging; or just use error_log instead (like in my case).


I am not sure though if this will prevent the memory exceeded problem. Eventually increase the PHP_MEMORY_LIMIT (via env variables if supported by your environment, or via ini_set()). And pay attention to use the correct unit symbol (M for Megabytes, G for Gigabytes), I so often write it wrongly as MB.

Kamafeather
  • 8,663
  • 14
  • 69
  • 99