From 8f9f44eb21d5cd29ce41277080b9e986a9da822a Mon Sep 17 00:00:00 2001 From: "tien.xuan.vo" Date: Mon, 16 Sep 2019 23:41:10 +0700 Subject: [PATCH] [Messenger] Fix exception message of failed message is dropped on retry --- .../FailedMessagesRetryCommandTest.php | 53 ++++++++++++++++--- src/Symfony/Component/Messenger/Worker.php | 16 +++++- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRetryCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRetryCommandTest.php index bcc67f79d5..bc69a1c2e8 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRetryCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRetryCommandTest.php @@ -13,12 +13,15 @@ namespace Symfony\Component\Messenger\Tests\Command; use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Debug\Exception\FlattenException; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Messenger\Command\FailedMessagesRetryCommand; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\MessageBusInterface; +use Symfony\Component\Messenger\Retry\RetryStrategyInterface; +use Symfony\Component\Messenger\Stamp\ReceivedStamp; +use Symfony\Component\Messenger\Stamp\RedeliveryStamp; use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface; -use Symfony\Component\Messenger\Worker; class FailedMessagesRetryCommandTest extends TestCase { @@ -34,16 +37,52 @@ class FailedMessagesRetryCommandTest extends TestCase // the bus should be called in the worker $bus->expects($this->exactly(2))->method('dispatch')->willReturn(new Envelope(new \stdClass())); - $command = new FailedMessagesRetryCommand( - 'failure_receiver', - $receiver, - $bus, - $dispatcher - ); + $command = new FailedMessagesRetryCommand('failure_receiver', $receiver, $bus, $dispatcher); $tester = new CommandTester($command); $tester->execute(['id' => [10, 12]]); $this->assertStringContainsString('[OK]', $tester->getDisplay()); } + + public function testExceptionOnRetry() + { + $receiver = $this->createMock(ListableReceiverInterface::class); + $receiver->expects($this->once())->method('find')->with(10)->willReturn(new Envelope(new \stdClass())); + // message will eventually be ack'ed in Worker + $receiver->expects($this->once())->method('ack'); + + $dispatcher = $this->createMock(EventDispatcherInterface::class); + $bus = $this->createMock(MessageBusInterface::class); + // the bus should be called in the worker + $bus->expects($this->at(0)) + ->method('dispatch') + ->with($this->callback(function (Envelope $envelope) { + $lastReceivedStamp = $envelope->last(ReceivedStamp::class); + + return $lastReceivedStamp instanceof ReceivedStamp && \is_string($lastReceivedStamp->getTransportName()); + })) + ->will($this->throwException(new \Exception('Mock test exception'))); + + $bus->expects($this->at(1)) + ->method('dispatch') + ->with($this->callback(function (Envelope $envelope) { + $lastRedeliveryStamp = $envelope->last(RedeliveryStamp::class); + + return $lastRedeliveryStamp instanceof RedeliveryStamp && + \is_string($lastRedeliveryStamp->getExceptionMessage()) && + $lastRedeliveryStamp->getFlattenException() instanceof FlattenException; + })) + ->willReturn(new Envelope(new \stdClass())); + + $retryStrategy = $this->createMock(RetryStrategyInterface::class); + $retryStrategy->expects($this->once())->method('isRetryable')->with($this->isInstanceOf(Envelope::class))->willReturn(true); + + $command = new FailedMessagesRetryCommand('failure_receiver', $receiver, $bus, $dispatcher, $retryStrategy); + + $tester = new CommandTester($command); + $tester->execute(['id' => [10]]); + + $this->assertStringContainsString('[OK]', $tester->getDisplay()); + } } diff --git a/src/Symfony/Component/Messenger/Worker.php b/src/Symfony/Component/Messenger/Worker.php index 6bfb4cb675..f463dde784 100644 --- a/src/Symfony/Component/Messenger/Worker.php +++ b/src/Symfony/Component/Messenger/Worker.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Messenger; use Psr\Log\LoggerInterface; +use Symfony\Component\Debug\Exception\FlattenException; use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent; use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent; use Symfony\Component\Messenger\Event\WorkerMessageReceivedEvent; @@ -152,7 +153,7 @@ class Worker implements WorkerInterface // add the delay and retry stamp info + remove ReceivedStamp $retryEnvelope = $envelope->with(new DelayStamp($delay)) - ->with(new RedeliveryStamp($retryCount, $transportName)) + ->with(new RedeliveryStamp($retryCount, $transportName, $throwable->getMessage(), $this->flattenedException($throwable))) ->withoutAll(ReceivedStamp::class); // re-send the message @@ -215,4 +216,17 @@ class Worker implements WorkerInterface return $retryStrategy->isRetryable($envelope); } + + private function flattenedException(\Throwable $throwable): ?FlattenException + { + if (!class_exists(FlattenException::class)) { + return null; + } + + if ($throwable instanceof HandlerFailedException) { + $throwable = $throwable->getNestedExceptions()[0]; + } + + return FlattenException::createFromThrowable($throwable); + } }