1

I have a Laravel application which is running 'swooletw/laravel-swoole' to handle requests. Doctrine is being used as ORM using laravel-doctrine/orm.

Normally if some ORMException is thrown by doctrine it closes the EntityManager which is supposed to be opened/reset automatically on next request. But while I am using swoole it does not simply happen and EntityManager remains closed unless the swoole worker is restarted.

I tried to fix this by checking if entitmanager is closed and resetting it in a Middleware. So on next request it should supply a new entity manager.

    protected function handleClosedManagers()
    {
        foreach (Registry::getManagerNames() as $managerName) {
            /** @var EntityManager $manager */
            $manager = Registry::getManager($managerName);
            if (!$manager->isOpen()) {
                Registry::resetManager($managerName);
                $manager = Registry::getManager($managerName);
            }
        }
    }

Ideally it should fix it but I am getting "EntityManager is Closed." exception.

I assumed that I need to refresh it from laravel container as well.

So i changed the code to this:

 protected function handleClosedManagers()
    {
        foreach (Registry::getManagerNames() as $managerName) {
            /** @var EntityManager $manager */
            $manager = Registry::getManager($managerName);
            if (!$manager->isOpen()) {
                Registry::resetManager($managerName);
                $manager = Registry::getManager($managerName);
                app()->forgetInstance('doctrine.managers.'.$managerName);
                app()->forgetInstance('mem');
                app()->forgetInstance(MagentoEntityManager::class);
                app()->singleton('doctrine.managers.'.$managerName, function () use ($manager) {
                    return $manager;
                });
                // we are using separate entitymanager than default one
                app()->singleton('mem', function () use ($manager) {
                    return $manager;
                });
            }
        }
    }

Now I am getting this on persist :

ErrorException: Undefined index: 000000005ba55063000000001b2f101d in file /***/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php on line 3003

Anyone know the proper way to handle this situation?

Irfan Amir
  • 11
  • 2
  • PHP and therefore Doctrine is not optimised for long-running processes, in order to reset the connection it is a better option to reload the workers using swoole_reload(), but be cautious swoole_reload will only reload the modified files not the files already in memory. Let me know if that helps and I will then convert it to an answer. – Aftab Naveed Jul 23 '19 at 21:18

1 Answers1

0

I faced same problem in a Zend Expressive app.

Instead of trying to replace closed Entity Manager in a container and all services where it could be already injected, I found it easier to make the Entity Manager resettable by wrapping it into a decorator, that allows to replace actual Entity Manager with a fresh instance when needed:

final class ResettableEntityManager extends \Doctrine\ORM\Decorator\EntityManagerDecorator
{
    /**
     * @var callable
     */
    private $entityManagerFactory;

    public function __construct(callable $entityManagerFactory)
    {
        $this->entityManagerFactory = $entityManagerFactory;
        parent::__construct($entityManagerFactory());
    }

    public function recreate(): void
    {
        $this->wrapped = ($this->entityManagerFactory)();
    }
}

At the end of each request I then recreate the Entity Manager if it's closed, otherwise I just clear it to prevent memory leaks:

if ($this->entityManager->isOpen()) {
    $this->entityManager->clear();
} else {
    $this->entityManager->recreate();
}
pauci
  • 1