feature #31334 [Messenger] Add clear Entity Manager middleware (Koc)

This PR was merged into the 4.4 branch.

Discussion
----------

[Messenger] Add clear Entity Manager middleware

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #29662
| License       | MIT
| Doc PR        | TBD

General purpose of this middleware:
* avoid memory leaks during messages handling
* prevent unexpected side effects when entities that already stored in identity map not refreshed between messages

Commits
-------

6e690a6078 Add clear Entity Manager middleware (closes #29662)
This commit is contained in:
Fabien Potencier 2019-06-04 07:41:58 +02:00
commit be7b7fea60
3 changed files with 113 additions and 0 deletions

View File

@ -1,6 +1,12 @@
CHANGELOG
=========
4.4.0
-----
* added `DoctrineClearEntityManagerMiddleware`
4.3.0
-----

View File

@ -0,0 +1,53 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bridge\Doctrine\Messenger;
use Doctrine\Common\Persistence\ManagerRegistry;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException;
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Middleware\StackInterface;
/**
* Clears entity manager after calling all handlers.
*
* @author Konstantin Myakshin <molodchick@gmail.com>
*/
class DoctrineClearEntityManagerMiddleware implements MiddlewareInterface
{
private $managerRegistry;
private $entityManagerName;
public function __construct(ManagerRegistry $managerRegistry, string $entityManagerName = null)
{
$this->managerRegistry = $managerRegistry;
$this->entityManagerName = $entityManagerName;
}
/**
* {@inheritdoc}
*/
public function handle(Envelope $envelope, StackInterface $stack): Envelope
{
try {
$entityManager = $this->managerRegistry->getManager($this->entityManagerName);
} catch (\InvalidArgumentException $e) {
throw new UnrecoverableMessageHandlingException($e->getMessage(), 0, $e);
}
try {
return $stack->next()->handle($envelope, $stack);
} finally {
$entityManager->clear();
}
}
}

View File

@ -0,0 +1,54 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bridge\Doctrine\Tests\Messenger;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\Messenger\DoctrineClearEntityManagerMiddleware;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException;
use Symfony\Component\Messenger\Test\Middleware\MiddlewareTestCase;
class DoctrineClearEntityManagerMiddlewareTest extends MiddlewareTestCase
{
public function testMiddlewareClearEntityManager()
{
$entityManager = $this->createMock(EntityManagerInterface::class);
$entityManager->expects($this->once())
->method('clear');
$managerRegistry = $this->createMock(ManagerRegistry::class);
$managerRegistry
->method('getManager')
->with('default')
->willReturn($entityManager);
$middleware = new DoctrineClearEntityManagerMiddleware($managerRegistry, 'default');
$middleware->handle(new Envelope(new \stdClass()), $this->getStackMock());
}
public function testInvalidEntityManagerThrowsException()
{
$managerRegistry = $this->createMock(ManagerRegistry::class);
$managerRegistry
->method('getManager')
->with('unknown_manager')
->will($this->throwException(new \InvalidArgumentException()));
$middleware = new DoctrineClearEntityManagerMiddleware($managerRegistry, 'unknown_manager');
$this->expectException(UnrecoverableMessageHandlingException::class);
$middleware->handle(new Envelope(new \stdClass()), $this->getStackMock(false));
}
}