Baldinof / roadrunner-bundle

A RoadRunner worker integrated in your Symfony app
MIT License
255 stars 46 forks source link

Weird doctrine errors that are not present in RR debug mode #104

Closed FluffyDiscord closed 1 year ago

FluffyDiscord commented 1 year ago

Hi, I am experiencing very random doctrine errors at production that do not show up in dev mode.

I would think that it is either because the doctrine integration is closing/clearing things too early or in the middle of requests or the integration "is sadly not doing enough".

I am using RR debug: true (reseter is deprecated) to develop and I get basically full app reboot on each request and none of the following exceptions show up.

On the production thought it's a different matter. I am getting the following errors but only after a bit of time or few requests:

- A new entity was found through the relationship 'ENTITY_NAME' that was not configured to cascade persist operations for entity: 
- Multiple non-persisted new entities were found through the given association graph: *
- A managed+dirty entity ENTITY_ID can not be scheduled for insertion.

Again, mind you that these errors are only in production environment.

Basically any operation with DB is doomed to fail and my app is unusable, the workaround is to reboot kernel on each request.

It feels like the doctrine entity manager and other related stuff is being shared across multiple requests at once and when one request closes/reopens/clears the EM, the other request that is still happening uses the same object reference and thus fails miserably to do anything.

Any thoughts please? It's pressing issue

FluffyDiscord commented 1 year ago

After a bit of debugging I can say that it's because symfony is not callling reset() on services that implement Symfony\Contracts\Service\ResetInterface.

I was caching entities in my services to reduce DB load and I expected the cache to be reset on each request, but since the method was not being called, entity manager was closed between requests (as this bundle does) but entities were left behind in my cache and the next requests mixed managed entities with these cached ones and voila, we have issues.

The obvious fix would be for this bundle to add it's own kernel.terminate listener or other means to call Symfony\Component\HttpKernel\DependencyInjectio\ServicesResetter::reset() manually that resets all services.

Eg.

<?php

namespace App\EventListener;

use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpKernel\DependencyInjection\ServicesResetter;
use Symfony\Component\HttpKernel\Event\TerminateEvent;
use Symfony\Component\HttpKernel\KernelEvents;

#[AsEventListener(event: KernelEvents::TERMINATE, priority: -1025)] // -1024 is profiler
class ServiceResetListener
{
    public function __construct(
        #[Autowire("@services_resetter")]
        private readonly ServicesResetter $servicesResetter,
    )
    {
    }

    public function __invoke(TerminateEvent $event): void
    {
        if ($event->isMainRequest() === false) {
            return;
        }

        $this->servicesResetter->reset();
    }
}