doctrine / DoctrineORMModule

Doctrine ORM Module for Laminas
https://www.doctrine-project.org/projects/doctrine-orm-module.html
MIT License
437 stars 229 forks source link

No way to reset EntityManager #755

Closed ghispi closed 1 month ago

ghispi commented 1 month ago

According to the doctrine documentation, if the transaction fails, you need to close EntityManager and rollback the transaction.

https://www.doctrine-project.org/projects/doctrine-orm/en/2.19/reference/transactions-and-concurrency.html#exception-handling https://www.doctrine-project.org/projects/doctrine-orm/en/3.2/reference/transactions-and-concurrency.html#exception-handling

Doctrine provides:

but these don't seem to be integrated with laminas in this module.

Is there a way to reset EM that I might have omitted or is it left to the individual to integrate the registry into the framework?

TomHAnderson commented 1 month ago

There are two points to your inquiry. First, are the registries integrated with DoctrineORMModule? No, they are not directly integrated.

The second is about what you're trying to accomplish. It seems to me you want to recreate a closed entity manager. Is that correct? This is not recommended and not supported. If the entity manager closes then the request should handle that failure and the application reset for the next request.

Some more detail about what you're trying to do, apart from using multiple entity managers with the registry, would be helpful.

ghispi commented 1 month ago

It seems to me you want to recreate a closed entity manager. Is that correct?

That is correct. Lets assume I've job queue based on doctrine. I've got transaction in a job which fails, I wanna update the job as failed.

This is not recommended and not supported. If the entity manager closes then the request should handle that failure and the application reset for the next request.

Doctrine doc mentions this:

If you intend to start another unit of work after an exception has occurred you should do that with a new EntityManager.

I'm coming here because I've seen that doctrine/DoctrineBundle & symfony/doctrine-bridge does that.

One can grab registry and reset EM.

It seems to be very impractical and inconvenient that it is not possible with Laminas integration. I can probably come with multiple scenarios where further processing is required after transaction failed, i.e. batch processing. This normally wouldn't be a problem when using DBAL or PDO directly.

Is there any workaround or way to achieve this? Only thing I can come with is separate EM for the queue and processing itself.

TomHAnderson commented 1 month ago

I've seen this problem before and it has always been related to logging in the database. You're running more than one queue item per worker too.

My recommendation, when I see this problem, is to never log to the database of record (the object manager's database). Additionally in your case, I would always run one queue item at a time per worker. Your queue seems to be running from the database, too. Using Rabbit MQ or similar will change that but the real problem is running more than one job per worker.

ghispi commented 1 month ago

I really appreciate the suggestions and advice but I am not in position to make a decision to change implementation.

It is not logging actually, but rather marking the job or rather some related object as failed. I have one job in the worker.

I do not have access to code atm but rough pseudo code would be:

function execute (): void
{
    $context = $jobItem->getContext();
    $object = $this->someObjRepository->getById($context['id']);
    try {
        $this->entityManager->wrapIntransaction(function(){
              // do some db ops based on the object
        });
   } (\Throwable $t) {
       // in symfony integration I could do:
       // $this->registry->resetManager();
       // and to my understanding that would reset the EM in container but also "fix" the current EM since it is proxied with Ocramius/ProxyManager
       $object->setStatus('failed');
       $object->setFailedAt(new \DateTimeImmutable());
       $this->entityManager->flush(); // obviously this will fail as EM is closed
   }
}

I hope this clarifies the issue I'm facing.

TomHAnderson commented 1 month ago

I did a lot of queue processing at one time and I used SlmQueueDoctrine. This tool, as you can see here, uses the Doctrine connection directly: https://github.com/Webador/SlmQueueDoctrine/blob/master/src/Queue/DoctrineQueue.php

Doing it this way, queries can continue to be executed if the entity manager fails.

ghispi commented 1 month ago

I believe we uses it, what you see in the example is the job execute method, implementation of JobInterface.

So the suggestion is to actually grab the connection from EM and update manually, somehow I didn't think about it as I was to focused on EM.

Thank you, have a great one.