doctrine / DoctrineBundle

Symfony Bundle for Doctrine ORM and DBAL
https://www.doctrine-project.org/projects/doctrine-bundle.html
MIT License
4.7k stars 449 forks source link

EntityListener not called since 2.2.0 under prod env only #1249

Closed PierrickMartos closed 3 years ago

PierrickMartos commented 3 years ago

Hello,

Since the release of doctrine-bundle:2.2.0, we are facing a strange issue with our EntityListeners not called only when symfony run under prod env. Note, It works normally on dev and test env.

To give you more context, our EntityListener is registered on a MappedSuperClasss and not directly on the final Entity class. But we never had an issue with this before the 2.2.0 release.

Here is how we register our EntityListener (the service is registered inside a vendor managed by us):

  SAM\DealFlowBundle\EventListener\DealFlowListener:
    arguments:
      - '%sam_entities%'
    tags:
      - { name: doctrine.orm.entity_listener, event: prePersist, entity: SAM\DealFlowBundle\Entity\DealFlow, method: prePersist, lazy: true, priority: 100 }

Here is the DealFlowListener:

<?php

namespace SAM\DealFlowBundle\EventListener;

use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use SAM\CommonBundle\Utils\LoggerUtils;
use SAM\DealFlowBundle\Entity\DealFlow;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

/**
 * class DealFlowListener.
 */
class DealFlowListener
{
    /**
     * @var TokenStorageInterface
     */
    private $tokenStorage;

    /**
     * @var array
     */
    protected $entities;

    /**
     * @var EntityManagerInterface
     */
    protected $em;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * DealFlowListener constructor.
     *
     * @param array                 $entities
     * @param TokenStorageInterface $tokenStorage
     */
    public function __construct($entities, TokenStorageInterface $tokenStorage, LoggerInterface $logger)
    {
        $this->tokenStorage = $tokenStorage;
        $this->entities = $entities;
        $this->logger = $logger;
    }

    /**
     * @param DealFlow                 $deal
     * @param LifecycleEventArgs $args
     *
     * @throws \Exception
     */
    public function prePersist(DealFlow $deal, LifecycleEventArgs $args)
    {
        $this->logger->info('[DealFlowListener] prePersist called');

        // Some code here removed from this snippet to keep it simple
}

The mappedsuperclass, DealFlow:

<?php

namespace SAM\DealFlowBundle\Entity;

use ...

/**
 * @ORM\Table(
 *     name="deal_flow",
 *     indexes={@ORM\Index(name="project_name_index", columns={"project_name"}, flags={"fulltext"})},
 *     uniqueConstraints={@ORM\UniqueConstraint(name="legal_entity_deal_flow", columns={"legal_entity_id", "legal_entity_deal_flow_id"})}
 * )
 * @ORM\MappedSuperclass()
 * @ORM\HasLifecycleCallbacks()
 *
 * @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false)
 */
class DealFlow
{
    // ...
}

And the DealFlow final entity:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use SAM\DealFlowBundle\Entity\DealFlow as BaseDealFlow;

/**
 * @ORM\Entity
 */
class DealFlow extends BaseDealFlow
{

}

Also, when i run bin/console --env=prod debug:container DealFlowListener on production, the service appears and it's registered into the container :

image

For now we have fix our composer.json to doctrine-bundle 2.1.2 and it works.

Our environment :

Our composer.json:

{
    "name": "*******",
    "description": "",
    "license": "proprietary",
    "type": "project",
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "App\\Tests\\": "tests/"
        }
    },
    "require": {
        "php": ">=7.2",
        "ext-ctype": "*",
        "ext-dom": "*",
        "ext-gd": "*",
        "ext-iconv": "*",
        "ext-intl": "*",
        "ext-json": "*",
        "ext-ldap": "*",
        "ext-pdo": "*",
        "ext-soap": "*",
        "ext-zip": "*",
        "ext-zlib": "*",
        "algolia/search-bundle": "^4.1",
        "anyx/login-gate-bundle": "^2.0",
        "beberlei/doctrineextensions": "^1.2",
        "cocur/slugify": "^4.0",
        "composer/package-versions-deprecated": "^1.8",
        "damienharper/doctrine-audit-bundle": "^3.4",
        "debril/rss-atom-bundle": "^5.0",
        "doctrine/common": "^2.11",
        "doctrine/doctrine-bundle": "^2.1",
        "doctrine/doctrine-fixtures-bundle": "^3.3",
        "doctrine/doctrine-migrations-bundle": "^3.0",
        "doctrine/orm": "^2.7",
        "eightpoints/guzzle-bundle": "^8.2",
        "endroid/qr-code": "^3.9",
        "endroid/qr-code-bundle": "^3.4",
        "enqueue/amqp-lib": "^0.9.12",
        "enqueue/enqueue-bundle": "^0.10.3",
        "evence/soft-deleteable-extension-bundle": "^2.0.0",
        "fresh/doctrine-enum-bundle": "^6.6",
        "friendsofsymfony/ckeditor-bundle": "^2.2",
        "friendsofsymfony/jsrouting-bundle": "^2.6",
        "friendsofsymfony/user-bundle": "^2.1",
        "fzaninotto/faker": "^1.9",
        "garethp/php-ews": "^0.9.9",
        "guzzlehttp/guzzle": "~6.0",
        "incenteev/composer-parameter-handler": "^2.1",
        "jeroendesloovere/vcard": "^1.7",
        "knplabs/knp-gaufrette-bundle": "^0.7.1",
        "knplabs/knp-paginator-bundle": "^5.3",
        "knplabs/knp-snappy-bundle": "^1.7",
        "knplabs/knp-time-bundle": "^1.13",
        "kunalvarma05/dropbox-php-sdk": "^0.2.1",
        "lexxpavlov/settingsbundle": "^1.5.1",
        "liip/imagine-bundle": "^2.3",
        "loilo/fuse": "^3.6",
        "monolog/monolog": "^1.3",
        "mysam/environment-symfony-tools": "^2.0",
        "mysam/sortable-behavior-bundle": "^1.5",
        "nyholm/effective-interest-rate": "^1.0",
        "ocramius/proxy-manager": "2.1.*",
        "phpoffice/phpspreadsheet": "^1.15",
        "portphp/spreadsheet": "^1.0",
        "scheb/two-factor-bundle": "^4.18",
        "sensio/framework-extra-bundle": "^5.6",
        "sentry/sentry-symfony": "^3.5",
        "sonata-project/admin-bundle": "^3.63",
        "sonata-project/doctrine-extensions": "^1.9",
        "sonata-project/doctrine-orm-admin-bundle": "^3.22",
        "sonata-project/exporter": "^2.4",
        "sonata-project/form-extensions": "^1.6",
        "sonata-project/twig-extensions": "^1.4",
        "stechstudio/backoff": "^1.1",
        "stof/doctrine-extensions-bundle": "^1.5",
        "suncat/mobile-detect-bundle": "dev-master",
        "symfony/asset": "4.4.*",
        "symfony/cache": "4.4.*",
        "symfony/console": "4.4.*",
        "symfony/dotenv": "4.4.*",
        "symfony/flex": "^1.3.1",
        "symfony/framework-bundle": "4.4.*",
        "symfony/ldap": "4.4.*",
        "symfony/lock": "4.4.*",
        "symfony/mailer": "4.4.*",
        "symfony/monolog-bundle": "^3.5",
        "symfony/polyfill-apcu": "^1.18",
        "symfony/property-info": "4.4.*",
        "symfony/proxy-manager-bridge": "4.4.*",
        "symfony/serializer": "4.4.*",
        "symfony/translation": "4.4.*",
        "symfony/twig-bundle": "4.4.*",
        "twig/extensions": "^1.5",
        "twig/extra-bundle": "^2.12|^3.0",
        "twig/twig": "^2.12",
        "vich/uploader-bundle": "^1.15"
    },
    "require-dev": {
        "dama/doctrine-test-bundle": "^6.3",
        "doctrine/data-fixtures": "^1.4",
        "escapestudios/symfony2-coding-standard": "^3.11",
        "hautelook/alice-bundle": "^2.7",
        "johnkary/phpunit-speedtrap": "^3.2",
        "liuggio/fastest": "^1.7",
        "phpunit/phpunit": "8.5.8",
        "sempro/phpunit-pretty-print": "^1.2",
        "symfony/browser-kit": "^4.4",
        "symfony/css-selector": "4.4.*",
        "symfony/debug-bundle": "^4.4",
        "symfony/maker-bundle": "^1.21",
        "symfony/phpunit-bridge": "^5.1",
        "symfony/stopwatch": "^4.4",
        "symfony/var-dumper": "^4.4",
        "symfony/web-profiler-bundle": "^4.4",
        "symplify/easy-coding-standard": "^8.3",
        "theofidry/alice-data-fixtures": "^1.2"
    },
    "repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/Software-For-Asset-Management/MobileDetectBundle"
        }
    ],
    "scripts": {
        "php-cs-fixer": "php vendor/friendsofphp/php-cs-fixer/php-cs-fixer fix vendor/sam/ --rules=@Symfony",
        "auto-scripts": {
            "cache:clear": "symfony-cmd",
            "ckeditor:install --clear=drop": "symfony-cmd",
            "assets:install --symlink --relative %PUBLIC_DIR%": "symfony-cmd",
            "sam:bin:install": "symfony-cmd",
            "assets:install %PUBLIC_DIR%": "symfony-cmd"
        },
        "post-install-cmd": [
            "@auto-scripts"
        ],
        "post-update-cmd": [
            "@auto-scripts"
        ]
    },
    "replace": {
        "paragonie/random_compat": "2.*",
        "symfony/polyfill-ctype": "*",
        "symfony/polyfill-iconv": "*",
        "symfony/polyfill-php71": "*",
        "symfony/polyfill-php70": "*",
        "symfony/polyfill-php56": "*"
    },
    "conflict": {
        "symfony/symfony": "*"
    },
    "config": {
        "preferred-install": {
            "*": "dist"
        },
        "sort-packages": true,
        "platform": {
            "php": "7.2.22"
        }
    },
    "extra": {
        "symfony": {
            "allow-contrib": true,
            "require": "4.4.*"
        }
    },
    "minimum-stability": "dev",
    "prefer-stable": true
}
ostrolucky commented 3 years ago

This might very well be relevant https://github.com/doctrine/DoctrineBundle/pull/1196. Can you check if reverting those changes fixes your issue?

PierrickMartos commented 3 years ago

Hi @ostrolucky , i confirm, by reverting the two commits it works.

Here is the repository with the revert commits: https://github.com/Software-For-Asset-Management/DoctrineBundle/tree/master

ostrolucky commented 3 years ago

Reproducer would be more useful. I'm unable to reproduce the issue. Tried with 2.2.0, under prod, with prePersist, with @ORM\MappedSuperclass, without @ORM\EntityListeners. Listener is triggered every time.

zubov-ruslan commented 3 years ago

Have the same issue with 2.2.0, with postPersist event. For reproduce just needed run cache warm up: bin/console cache:warmup --env=prod --no-debug

image

ostrolucky commented 3 years ago

Indeed, I've reproduced this now

dmaicher commented 3 years ago

@zubov-ruslan how exactly did you produce this diff in the screenshot? So in one default_metadata.php file the entitylisteners are present and in the other one not?

So far I can reproduce the missing case. But I cannot get it to show entitylisteners with 2.2.0 on my project

zubov-ruslan commented 3 years ago

@dmaicher it happens when you run bin/console cache:warmup --env=prod --no-debug for the second time

ostrolucky commented 3 years ago

We decided to rollback the change which caused this issue. We released 2.2.1 which should fix your issue.

ossinkine commented 3 years ago

@dmaicher Did you be able to reproduce? @zubov-ruslan I can't reproduce even after run cache warmup multiple times. Do you do something between two calls of cache warmup? What is your metadata_cache_driver configuration? Do you use one or several entity managers?

ostrolucky commented 3 years ago

Crucial part for reproducing is not defining @ORM\EntityListeners, listener needs to be solely defined via service definition only.

ossinkine commented 3 years ago

@ostrolucky I've changed the listener definition from annotations to services, but everything still works. Just difference is a new service listener appears in the container.

ostrolucky commented 3 years ago

bug is only in prod mode, with warmed up cache, since that's where feature works

ossinkine commented 3 years ago

I've reproduced this. When entity listeners declared using services config they are added to metadata using event manager in AttachEntityListenersListener. When we warming up the php array cache we creating a clear ClassMetadataFactory with no event listeners. Using ClassMetadataFactory from container should fix this, I'll try to create a fix.

ossinkine commented 3 years ago

Since AttachEntityListenersListener attach listener only once the best solution I see is to set the highest priority to DoctrineMetadataCacheWarmer. @PierrickMartos @zubov-ruslan Could you please check this solution, please checkout to 2.2.0 and change this line ->addTag('kernel.cache_warmer'); to ->addTag('kernel.cache_warmer', ['priority' => 1000]);?

ostrolucky commented 3 years ago

I would really suggest to give us a feedback and try the suggested changes if you don't want us to break your application again in next release.

PierrickMartos commented 3 years ago

I try to find a moment to reproduce it in coming days

PierrickMartos commented 3 years ago

Hi @ossinkine i've made some tests, i confirm that it doesnt work when changing ->addTag('kernel.cache_warmer'); to ->addTag('kernel.cache_warmer', ['priority' => 1000]);