Skip to content

Commit 72f2480

Browse files
committed
[Messenger] refactoring
1 parent 662b35e commit 72f2480

29 files changed

+252
-596
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,20 +1834,29 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder
18341834
}
18351835
}
18361836

1837+
$sendersServiceLocator = ServiceLocatorTagPass::register($container, $senderReferences);
1838+
18371839
$container->getDefinition('messenger.senders_locator')
18381840
->replaceArgument(0, $messageToSendersMapping)
1839-
->replaceArgument(1, ServiceLocatorTagPass::register($container, $senderReferences))
1841+
->replaceArgument(1, $sendersServiceLocator)
1842+
;
1843+
1844+
$container->getDefinition('messenger.retry.send_failed_message_for_retry_listener')
1845+
->replaceArgument(0, $sendersServiceLocator)
18401846
;
18411847

18421848
$container->getDefinition('messenger.retry_strategy_locator')
18431849
->replaceArgument(0, $transportRetryReferences);
18441850

18451851
if ($config['failure_transport']) {
1852+
if (!isset($senderReferences[$config['failure_transport']])) {
1853+
throw new LogicException(sprintf('Invalid Messenger configuration: the failure transport "%s" is not a valid transport or service id.', $config['failure_transport']));
1854+
}
1855+
18461856
$container->getDefinition('messenger.failure.send_failed_message_to_failure_transport_listener')
1847-
->replaceArgument(1, $config['failure_transport']);
1857+
->replaceArgument(0, $senderReferences[$config['failure_transport']]);
18481858
$container->getDefinition('console.command.messenger_failed_messages_retry')
1849-
->replaceArgument(0, $config['failure_transport'])
1850-
->replaceArgument(4, $transportRetryReferences[$config['failure_transport']] ?? null);
1859+
->replaceArgument(0, $config['failure_transport']);
18511860
$container->getDefinition('console.command.messenger_failed_messages_show')
18521861
->replaceArgument(0, $config['failure_transport']);
18531862
$container->getDefinition('console.command.messenger_failed_messages_remove')

src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@
8686
<argument type="service" id="messenger.receiver_locator" />
8787
<argument type="service" id="logger" on-invalid="null" />
8888
<argument type="collection" /> <!-- Receiver names -->
89-
<argument type="service" id="messenger.retry_strategy_locator" />
9089
<argument type="service" id="event_dispatcher" />
9190
<call method="setCachePoolForRestartSignal">
9291
<argument type="service" id="cache.messenger.restart_workers_signal" />
@@ -116,25 +115,24 @@
116115

117116
<service id="console.command.messenger_failed_messages_retry" class="Symfony\Component\Messenger\Command\FailedMessagesRetryCommand">
118117
<argument /> <!-- Receiver name -->
119-
<argument /> <!-- Receiver locator -->
118+
<argument /> <!-- Receiver -->
120119
<argument type="service" id="messenger.routable_message_bus" />
121120
<argument type="service" id="event_dispatcher" />
122-
<argument /> <!-- Retry strategy -->
123121
<argument type="service" id="logger" />
124122

125123
<tag name="console.command" command="messenger:failed:retry" />
126124
</service>
127125

128126
<service id="console.command.messenger_failed_messages_show" class="Symfony\Component\Messenger\Command\FailedMessagesShowCommand">
129127
<argument /> <!-- Receiver name -->
130-
<argument /> <!-- Receiver locator -->
128+
<argument /> <!-- Receiver -->
131129

132130
<tag name="console.command" command="messenger:failed:show" />
133131
</service>
134132

135133
<service id="console.command.messenger_failed_messages_remove" class="Symfony\Component\Messenger\Command\FailedMessagesRemoveCommand">
136134
<argument /> <!-- Receiver name -->
137-
<argument /> <!-- Receiver locator -->
135+
<argument /> <!-- Receiver -->
138136

139137
<tag name="console.command" command="messenger:failed:remove" />
140138
</service>

src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.xml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<!-- Asynchronous -->
1111
<service id="messenger.senders_locator" class="Symfony\Component\Messenger\Transport\Sender\SendersLocator">
1212
<argument type="collection" /> <!-- Per message senders map -->
13-
<argument /> <!-- senders locator -->
13+
<argument /> <!-- senders service locator -->
1414
</service>
1515
<service id="messenger.middleware.send_message" class="Symfony\Component\Messenger\Middleware\SendMessageMiddleware">
1616
<tag name="monolog.logger" channel="messenger" />
@@ -93,12 +93,19 @@
9393
<argument /> <!-- max delay ms -->
9494
</service>
9595

96+
<service id="messenger.retry.send_failed_message_for_retry_listener" class="Symfony\Component\Messenger\EventListener\SendFailedMessageForRetryListener">
97+
<tag name="kernel.event_subscriber" />
98+
<tag name="monolog.logger" channel="messenger" />
99+
<argument /> <!-- senders service locator -->
100+
<argument type="service" id="messenger.retry_strategy_locator" />
101+
<argument type="service" id="logger" on-invalid="ignore" />
102+
</service>
103+
96104
<!-- failed handling -->
97105
<service id="messenger.failure.send_failed_message_to_failure_transport_listener" class="Symfony\Component\Messenger\EventListener\SendFailedMessageToFailureTransportListener">
98106
<tag name="kernel.event_subscriber" />
99107
<tag name="monolog.logger" channel="messenger" />
100-
<argument type="service" id="messenger.routable_message_bus" />
101-
<argument /> <!-- Failure transport name -->
108+
<argument /> <!-- Failure transport -->
102109
<argument type="service" id="logger" on-invalid="ignore" />
103110
</service>
104111

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"symfony/http-client": "^4.4|^5.0",
4646
"symfony/lock": "^4.4|^5.0",
4747
"symfony/mailer": "^4.4|^5.0",
48-
"symfony/messenger": "^4.3.6|^5.0",
48+
"symfony/messenger": "^4.4|^5.0",
4949
"symfony/mime": "^4.4|^5.0",
5050
"symfony/process": "^3.4|^4.0|^5.0",
5151
"symfony/security-csrf": "^3.4|^4.0|^5.0",
@@ -77,7 +77,7 @@
7777
"symfony/form": "<4.3",
7878
"symfony/lock": "<4.4",
7979
"symfony/mailer": "<4.4",
80-
"symfony/messenger": "<4.3.6",
80+
"symfony/messenger": "<4.4",
8181
"symfony/mime": "<4.4",
8282
"symfony/property-info": "<3.4",
8383
"symfony/security-bundle": "<4.4",

src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ protected function displaySingleMessage(Envelope $envelope, SymfonyStyle $io)
8989
$redeliveryStamps = $envelope->all(RedeliveryStamp::class);
9090
$io->writeln(' Message history:');
9191
foreach ($redeliveryStamps as $redeliveryStamp) {
92-
$io->writeln(sprintf(' * Message failed and redelivered to the <info>%s</info> transport at <info>%s</info>', $redeliveryStamp->getSenderClassOrAlias(), $redeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s')));
92+
$io->writeln(sprintf(' * Message failed at <info>%s</info>', $redeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s')));
9393
}
9494
$io->newLine();
9595

src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,14 @@ class ConsumeMessagesCommand extends Command
4242
private $receiverLocator;
4343
private $logger;
4444
private $receiverNames;
45-
private $retryStrategyLocator;
4645
private $eventDispatcher;
4746
/** @var CacheItemPoolInterface|null */
4847
private $restartSignalCachePool;
4948

5049
/**
5150
* @param RoutableMessageBus $routableBus
5251
*/
53-
public function __construct($routableBus, ContainerInterface $receiverLocator, LoggerInterface $logger = null, array $receiverNames = [], /* ContainerInterface */ $retryStrategyLocator = null, EventDispatcherInterface $eventDispatcher = null)
52+
public function __construct($routableBus, ContainerInterface $receiverLocator, LoggerInterface $logger = null, array $receiverNames = [], /* EventDispatcherInterface */ $eventDispatcher = null)
5453
{
5554
if ($routableBus instanceof ContainerInterface) {
5655
@trigger_error(sprintf('Passing a "%s" instance as first argument to "%s()" is deprecated since Symfony 4.4, pass a "%s" instance instead.', ContainerInterface::class, __METHOD__, RoutableMessageBus::class), E_USER_DEPRECATED);
@@ -59,17 +58,16 @@ public function __construct($routableBus, ContainerInterface $receiverLocator, L
5958
throw new \TypeError(sprintf('The first argument must be an instance of "%s".', RoutableMessageBus::class));
6059
}
6160

62-
if (\is_array($retryStrategyLocator)) {
63-
@trigger_error(sprintf('The 5th argument of the class "%s" should be a retry-strategy locator, an array of bus names as a value is deprecated since Symfony 4.3.', __CLASS__), E_USER_DEPRECATED);
61+
if (null !== $eventDispatcher && !$eventDispatcher instanceof EventDispatcherInterface) {
62+
@trigger_error(sprintf('The 5th argument of the class "%s" should be a "%s"', __CLASS__, EventDispatcherInterface::class), E_USER_DEPRECATED);
6463

65-
$retryStrategyLocator = null;
64+
$eventDispatcher = null;
6665
}
6766

6867
$this->routableBus = $routableBus;
6968
$this->receiverLocator = $receiverLocator;
7069
$this->logger = $logger;
7170
$this->receiverNames = $receiverNames;
72-
$this->retryStrategyLocator = $retryStrategyLocator;
7371
$this->eventDispatcher = $eventDispatcher;
7472

7573
parent::__construct();
@@ -166,7 +164,6 @@ protected function execute(InputInterface $input, OutputInterface $output)
166164
}
167165

168166
$receivers = [];
169-
$retryStrategies = [];
170167
foreach ($receiverNames = $input->getArgument('receivers') as $receiverName) {
171168
if (!$this->receiverLocator->has($receiverName)) {
172169
$message = sprintf('The receiver "%s" does not exist.', $receiverName);
@@ -177,17 +174,12 @@ protected function execute(InputInterface $input, OutputInterface $output)
177174
throw new RuntimeException($message);
178175
}
179176

180-
if (null !== $this->retryStrategyLocator && !$this->retryStrategyLocator->has($receiverName)) {
181-
throw new RuntimeException(sprintf('Receiver "%s" does not have a configured retry strategy.', $receiverName));
182-
}
183-
184177
$receivers[$receiverName] = $this->receiverLocator->get($receiverName);
185-
$retryStrategies[$receiverName] = null !== $this->retryStrategyLocator ? $this->retryStrategyLocator->get($receiverName) : null;
186178
}
187179

188180
$bus = $input->getOption('bus') ? $this->routableBus->getMessageBus($input->getOption('bus')) : $this->routableBus;
189181

190-
$worker = new Worker($receivers, $bus, $retryStrategies, $this->eventDispatcher, $this->logger);
182+
$worker = new Worker($receivers, $bus, $this->eventDispatcher, $this->logger);
191183
$stopsWhen = [];
192184
if ($limit = $input->getOption('limit')) {
193185
$stopsWhen[] = "processed {$limit} messages";

src/Symfony/Component/Messenger/Command/FailedMessagesRetryCommand.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
use Symfony\Component\Messenger\Event\WorkerMessageReceivedEvent;
2525
use Symfony\Component\Messenger\Exception\LogicException;
2626
use Symfony\Component\Messenger\MessageBusInterface;
27-
use Symfony\Component\Messenger\Retry\RetryStrategyInterface;
2827
use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface;
2928
use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface;
3029
use Symfony\Component\Messenger\Transport\Receiver\SingleMessageReceiver;
@@ -39,14 +38,12 @@ class FailedMessagesRetryCommand extends AbstractFailedMessagesCommand
3938

4039
private $eventDispatcher;
4140
private $messageBus;
42-
private $retryStrategy;
4341
private $logger;
4442

45-
public function __construct(string $receiverName, ReceiverInterface $receiver, MessageBusInterface $messageBus, EventDispatcherInterface $eventDispatcher, RetryStrategyInterface $retryStrategy = null, LoggerInterface $logger = null)
43+
public function __construct(string $receiverName, ReceiverInterface $receiver, MessageBusInterface $messageBus, EventDispatcherInterface $eventDispatcher, LoggerInterface $logger = null)
4644
{
4745
$this->eventDispatcher = $eventDispatcher;
4846
$this->messageBus = $messageBus;
49-
$this->retryStrategy = $retryStrategy;
5047
$this->logger = $logger;
5148

5249
parent::__construct($receiverName, $receiver);
@@ -180,7 +177,6 @@ private function runWorker(ReceiverInterface $receiver, SymfonyStyle $io, bool $
180177
$worker = new Worker(
181178
[$this->getReceiverName() => $receiver],
182179
$this->messageBus,
183-
[$this->getReceiverName() => $this->retryStrategy],
184180
$this->eventDispatcher,
185181
$this->logger
186182
);

src/Symfony/Component/Messenger/Event/WorkerMessageFailedEvent.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,11 @@
2121
final class WorkerMessageFailedEvent extends AbstractWorkerMessageEvent
2222
{
2323
private $throwable;
24-
private $willRetry;
24+
private $willRetry = false;
2525

26-
public function __construct(Envelope $envelope, string $receiverName, \Throwable $error, bool $willRetry)
26+
public function __construct(Envelope $envelope, string $receiverName, \Throwable $error)
2727
{
2828
$this->throwable = $error;
29-
$this->willRetry = $willRetry;
3029

3130
parent::__construct($envelope, $receiverName);
3231
}
@@ -40,4 +39,9 @@ public function willRetry(): bool
4039
{
4140
return $this->willRetry;
4241
}
42+
43+
public function setForRetry(): void
44+
{
45+
$this->willRetry = true;
46+
}
4347
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?php
2+
/*
3+
* This file is part of the Symfony package.
4+
*
5+
* (c) Fabien Potencier <fabien@symfony.com>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
11+
namespace Symfony\Component\Messenger\EventListener;
12+
13+
use Psr\Container\ContainerInterface;
14+
use Psr\Log\LoggerInterface;
15+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
16+
use Symfony\Component\Messenger\Envelope;
17+
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
18+
use Symfony\Component\Messenger\Exception\HandlerFailedException;
19+
use Symfony\Component\Messenger\Exception\RuntimeException;
20+
use Symfony\Component\Messenger\Exception\UnrecoverableExceptionInterface;
21+
use Symfony\Component\Messenger\Retry\RetryStrategyInterface;
22+
use Symfony\Component\Messenger\Stamp\DelayStamp;
23+
use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
24+
use Symfony\Component\Messenger\Transport\Sender\SenderInterface;
25+
26+
/**
27+
* @author Tobias Schultze <http://tobion.de>
28+
*/
29+
class SendFailedMessageForRetryListener implements EventSubscriberInterface
30+
{
31+
private $sendersLocator;
32+
private $retryStrategyLocator;
33+
private $logger;
34+
35+
public function __construct(ContainerInterface $sendersLocator, ContainerInterface $retryStrategyLocator, LoggerInterface $logger = null)
36+
{
37+
$this->sendersLocator = $sendersLocator;
38+
$this->retryStrategyLocator = $retryStrategyLocator;
39+
$this->logger = $logger;
40+
}
41+
42+
public function onMessageFailed(WorkerMessageFailedEvent $event)
43+
{
44+
$retryStrategy = $this->getRetryStrategyForTransport($event->getReceiverName());
45+
$envelope = $event->getEnvelope();
46+
$throwable = $event->getThrowable();
47+
48+
$message = $envelope->getMessage();
49+
$context = [
50+
'message' => $message,
51+
'class' => \get_class($message),
52+
];
53+
54+
$shouldRetry = $retryStrategy && $this->shouldRetry($throwable, $envelope, $retryStrategy);
55+
56+
$retryCount = RedeliveryStamp::getRetryCountFromEnvelope($envelope);
57+
if ($shouldRetry) {
58+
$event->setForRetry();
59+
60+
++$retryCount;
61+
$delay = $retryStrategy->getWaitingTime($envelope);
62+
if (null !== $this->logger) {
63+
$this->logger->error('Error thrown while handling message {class}. Sending for retry #{retryCount} using {delay} ms delay. Error: "{error}"', $context + ['retryCount' => $retryCount, 'delay' => $delay, 'error' => $throwable->getMessage(), 'exception' => $throwable]);
64+
}
65+
66+
// add the delay and retry stamp info + remove ReceivedStamp
67+
$retryEnvelope = $envelope->with(new DelayStamp($delay))
68+
->with(new RedeliveryStamp($retryCount));
69+
70+
// re-send the message for retry
71+
$this->getSenderForTransport($event->getReceiverName())->send($retryEnvelope);
72+
} else {
73+
if (null !== $this->logger) {
74+
$this->logger->critical('Error thrown while handling message {class}. Removing from transport after {retryCount} retries. Error: "{error}"', $context + ['retryCount' => $retryCount, 'error' => $throwable->getMessage(), 'exception' => $throwable]);
75+
}
76+
}
77+
}
78+
79+
public static function getSubscribedEvents()
80+
{
81+
return [
82+
// must have higher priority than SendFailedMessageToFailureTransportListener
83+
WorkerMessageFailedEvent::class => ['onMessageFailed', 100],
84+
];
85+
}
86+
87+
private function shouldRetry(\Throwable $e, Envelope $envelope, RetryStrategyInterface $retryStrategy): bool
88+
{
89+
// if ALL nested Exceptions are an instance of UnrecoverableExceptionInterface we should not retry
90+
if ($e instanceof HandlerFailedException) {
91+
$shouldNotRetry = true;
92+
foreach ($e->getNestedExceptions() as $nestedException) {
93+
if (!$nestedException instanceof UnrecoverableExceptionInterface) {
94+
$shouldNotRetry = false;
95+
break;
96+
}
97+
}
98+
if ($shouldNotRetry) {
99+
return false;
100+
}
101+
}
102+
103+
if ($e instanceof UnrecoverableExceptionInterface) {
104+
return false;
105+
}
106+
107+
return $retryStrategy->isRetryable($envelope);
108+
}
109+
110+
private function getRetryStrategyForTransport(string $alias): ?RetryStrategyInterface
111+
{
112+
if ($this->retryStrategyLocator->has($alias)) {
113+
return $this->retryStrategyLocator->get($alias);
114+
}
115+
116+
return null;
117+
}
118+
119+
private function getSenderForTransport(string $alias): SenderInterface
120+
{
121+
if ($this->sendersLocator->has($alias)) {
122+
return $this->sendersLocator->get($alias);
123+
}
124+
125+
throw new RuntimeException(sprintf('Could not find sender "%s" based on the same receiver to send the failed message to for retry.', $alias));
126+
}
127+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy