Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3791cd8

Browse files
committed
messenger: multiple failed transports support
[Messenger] Multiple failure transports support fix case when there is no failure transport defined avoid BC
1 parent 9f93a38 commit 3791cd8

14 files changed

+398
-9
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,10 @@ function ($a) {
11371137
->prototype('variable')
11381138
->end()
11391139
->end()
1140+
->scalarNode('failure_transport')
1141+
->defaultNull()
1142+
->info('Transport name to send failed messages to (after all retries have failed).')
1143+
->end()
11401144
->arrayNode('retry_strategy')
11411145
->addDefaultsIfNotSet()
11421146
->beforeNormalization()

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

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,7 +1694,6 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder
16941694
}
16951695

16961696
$sendersServiceLocator = ServiceLocatorTagPass::register($container, $senderReferences);
1697-
16981697
$container->getDefinition('messenger.senders_locator')
16991698
->replaceArgument(0, $messageToSendersMapping)
17001699
->replaceArgument(1, $sendersServiceLocator)
@@ -1707,25 +1706,56 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder
17071706
$container->getDefinition('messenger.retry_strategy_locator')
17081707
->replaceArgument(0, $transportRetryReferences);
17091708

1709+
1710+
$failureTransports = [];
1711+
$hasAnyFailureTransport = false;
1712+
$failureTransportsServiceLocatorId = 'messenger.failure_transports.locator';
17101713
if ($config['failure_transport']) {
17111714
if (!isset($senderReferences[$config['failure_transport']])) {
17121715
throw new LogicException(sprintf('Invalid Messenger configuration: the failure transport "%s" is not a valid transport or service id.', $config['failure_transport']));
17131716
}
17141717

1718+
$hasAnyFailureTransport = true;
17151719
$container->getDefinition('messenger.failure.send_failed_message_to_failure_transport_listener')
17161720
->replaceArgument(0, $senderReferences[$config['failure_transport']]);
1721+
$container->getDefinition('messenger.failure.send_failed_message_to_failure_transport_listener')
1722+
->replaceArgument(2, null);
1723+
17171724
$container->getDefinition('console.command.messenger_failed_messages_retry')
17181725
->replaceArgument(0, $config['failure_transport']);
17191726
$container->getDefinition('console.command.messenger_failed_messages_show')
17201727
->replaceArgument(0, $config['failure_transport']);
17211728
$container->getDefinition('console.command.messenger_failed_messages_remove')
17221729
->replaceArgument(0, $config['failure_transport']);
17231730
} else {
1724-
$container->removeDefinition('messenger.failure.send_failed_message_to_failure_transport_listener');
17251731
$container->removeDefinition('console.command.messenger_failed_messages_retry');
17261732
$container->removeDefinition('console.command.messenger_failed_messages_show');
17271733
$container->removeDefinition('console.command.messenger_failed_messages_remove');
17281734
}
1735+
1736+
foreach ($config['transports'] as $name => $transport) {
1737+
if ($transport['failure_transport']) {
1738+
if (!isset($config['transports'][$transport['failure_transport']])) {
1739+
throw new LogicException(sprintf('Invalid Messenger configuration: the failure transport "%s" is not a valid transport or service id.', $transport['failure_transport']));
1740+
}
1741+
1742+
$hasAnyFailureTransport = true;
1743+
$failureTransports[$name] = $senderReferences[$transport['failure_transport']];
1744+
}
1745+
}
1746+
1747+
if ($hasAnyFailureTransport) {
1748+
$failureTransportsServiceLocator = ServiceLocatorTagPass::register($container, $failureTransports, $failureTransportsServiceLocatorId);
1749+
$container->getDefinition($failureTransportsServiceLocatorId)
1750+
->replaceArgument(0, $failureTransports)
1751+
->replaceArgument(1, $failureTransportsServiceLocator);
1752+
$container->getDefinition('messenger.failure.send_failed_message_to_failure_transport_listener')
1753+
->replaceArgument(0, $senderReferences[$config['failure_transport']] ?? null);
1754+
$container->getDefinition('messenger.failure.send_failed_message_to_failure_transport_listener')
1755+
->replaceArgument(2, count($failureTransports) > 0 ? $failureTransportsServiceLocator : null);
1756+
} else {
1757+
$container->removeDefinition('messenger.failure.send_failed_message_to_failure_transport_listener');
1758+
}
17291759
}
17301760

17311761
private function registerCacheConfiguration(array $config, ContainerBuilder $container)

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@
8585
<tag name="kernel.reset" method="reset" />
8686
</service>
8787

88+
<!-- failure transports locator per transport -->
89+
<service id="messenger.failure_transports.locator" class="Symfony\Component\DependencyInjection\ServiceLocator">
90+
<tag name="container.service_locator" />
91+
<argument type="collection" /> <!-- failure transports map by transport -->
92+
<argument /> <!-- failure transports locator -->
93+
</service>
94+
8895
<!-- retry -->
8996
<service id="messenger.retry_strategy_locator">
9097
<tag name="container.service_locator" />
@@ -110,8 +117,9 @@
110117
<service id="messenger.failure.send_failed_message_to_failure_transport_listener" class="Symfony\Component\Messenger\EventListener\SendFailedMessageToFailureTransportListener">
111118
<tag name="kernel.event_subscriber" />
112119
<tag name="monolog.logger" channel="messenger" />
113-
<argument /> <!-- Failure transport -->
120+
<argument /> <!-- Global Failure transport -->
114121
<argument type="service" id="logger" on-invalid="ignore" />
122+
<argument /> <!-- Failure transport inside each Transport -->
115123
</service>
116124

117125
<service id="messenger.listener.dispatch_pcntl_signal_listener" class="Symfony\Component\Messenger\EventListener\DispatchPcntlSignalListener">

src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@
412412
<xsd:element name="bus" type="messenger_bus" minOccurs="0" maxOccurs="unbounded" />
413413
</xsd:sequence>
414414
<xsd:attribute name="default-bus" type="xsd:string" />
415-
<xsd:attribute name="enabled" type="xsd:boolean" />
415+
<xsd:attribute name="failure_transport" type="xsd:string" />
416416
</xsd:complexType>
417417

418418
<xsd:complexType name="messenger_serializer">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
$container->loadFromExtension('framework', [
4+
'messenger' => [
5+
'transports' => [
6+
'transport_1' => [
7+
'dsn' => 'null://',
8+
'failure_transport' => 'failure_transport_1'
9+
],
10+
'transport_2' => 'null://',
11+
'transport_3' => [
12+
'dsn' => 'null://',
13+
'failure_transport' => 'failure_transport_3'
14+
],
15+
'failure_transport_1' => 'null://',
16+
'failure_transport_3' => 'null://'
17+
],
18+
],
19+
]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
$container->loadFromExtension('framework', [
4+
'messenger' => [
5+
'failure_transport' => 'failure_transport_global',
6+
'transports' => [
7+
'transport_1' => [
8+
'dsn' => 'null://',
9+
'failure_transport' => 'failure_transport_1'
10+
],
11+
'transport_2' => 'null://',
12+
'transport_3' => [
13+
'dsn' => 'null://',
14+
'failure_transport' => 'failure_transport_3'
15+
],
16+
'failure_transport_global' => 'null://',
17+
'failure_transport_1' => 'null://',
18+
'failure_transport_3' => 'null://',
19+
],
20+
],
21+
]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<container xmlns="http://symfony.com/schema/dic/services"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:framework="http://symfony.com/schema/dic/symfony"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
6+
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
7+
8+
<framework:config>
9+
<framework:messenger>
10+
<framework:transport name="transport_1" dsn="null://" failure_transport="failure_transport_1" />
11+
<framework:transport name="transport_2" dsn="null://" />
12+
<framework:transport name="transport_3" dsn="null://" failure_transport="failure_transport_3" />
13+
<framework:transport name="failure_transport_1" dsn="null://" />
14+
<framework:transport name="failure_transport_3" dsn="null://" />
15+
</framework:messenger>
16+
</framework:config>
17+
</container>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<container xmlns="http://symfony.com/schema/dic/services"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:framework="http://symfony.com/schema/dic/symfony"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
6+
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
7+
8+
<framework:config>
9+
<framework:messenger failure_transport="failure_transport_global">
10+
<framework:transport name="transport_1" dsn="null://" failure_transport="failure_transport_1" />
11+
<framework:transport name="transport_2" dsn="null://" />
12+
<framework:transport name="transport_3" dsn="null://" failure_transport="failure_transport_3" />
13+
<framework:transport name="failure_transport_global" dsn="null://" />
14+
<framework:transport name="failure_transport_1" dsn="null://" />
15+
<framework:transport name="failure_transport_3" dsn="null://" />
16+
</framework:messenger>
17+
</framework:config>
18+
</container>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
framework:
2+
messenger:
3+
transports:
4+
transport_1:
5+
dsn: 'null://'
6+
failure_transport: failure_transport_1
7+
transport_2: 'null://'
8+
transport_3:
9+
dsn: 'null://'
10+
failure_transport: failure_transport_3
11+
failure_transport_1: 'null://'
12+
failure_transport_3: 'null://'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
framework:
2+
messenger:
3+
failure_transport: failure_transport_global
4+
transports:
5+
transport_1:
6+
dsn: 'null://'
7+
failure_transport: failure_transport_1
8+
transport_2: 'null://'
9+
transport_3:
10+
dsn: 'null://'
11+
failure_transport: failure_transport_3
12+
failure_transport_global: 'null://'
13+
failure_transport_1: 'null://'
14+
failure_transport_3: 'null://'

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,73 @@ public function testMessenger()
597597
$this->assertSame(TransportFactory::class, $container->getDefinition('messenger.transport_factory')->getClass());
598598
}
599599

600+
public function testMessengerMultipleFailureTransports()
601+
{
602+
$container = $this->createContainerFromFile('messenger_multiple_failure_transports');
603+
$failureTransportsLocatorDefinition = $container->getDefinition('messenger.failure_transports.locator');
604+
605+
/** @var Reference $failureTransportsMapping */
606+
$failureTransportsMapping = $failureTransportsLocatorDefinition->getArgument(0);
607+
608+
// transport 2 exists but does not appear in the mapping
609+
$expectedFailureTransportsMapping = [
610+
'transport_1' => 'failure_transport_1',
611+
'transport_3' => 'failure_transport_3',
612+
];
613+
614+
$failedTransports = [
615+
'failure_transport_1',
616+
'failure_transport_3',
617+
];
618+
619+
foreach ($failureTransportsMapping as $transportName => $ref) {
620+
if (\in_array($transportName, $failedTransports)) {
621+
continue;
622+
}
623+
624+
$this->assertSame('messenger.transport.'.$expectedFailureTransportsMapping[$transportName], (string) $ref, sprintf('The transport "%s" does not have the expected failed transport reference', $transportName));
625+
}
626+
627+
$failedMessageTransportListenerReference =
628+
$container->getDefinition('messenger.failure.send_failed_message_to_failure_transport_listener');
629+
$this->assertNull($failedMessageTransportListenerReference->getArgument(0));
630+
$this->assertSame($failureTransportsLocatorDefinition->getArgument(1), $failedMessageTransportListenerReference->getArgument(2));
631+
}
632+
633+
public function testMessengerMultipleFailureTransportsWithGlobalFailureTransport()
634+
{
635+
$container = $this->createContainerFromFile('messenger_multiple_failure_transports_global');
636+
$failureTransportsLocatorDefinition = $container->getDefinition('messenger.failure_transports.locator');
637+
638+
/** @var Reference $failureTransportsMapping */
639+
$failureTransportsMapping = $failureTransportsLocatorDefinition->getArgument(0);
640+
641+
$expectedFailureTransportsMapping = [
642+
'transport_1' => 'failure_transport_1',
643+
'transport_2' => 'failure_transport_global',
644+
'transport_3' => 'failure_transport_3',
645+
];
646+
647+
$failed_transports = [
648+
'failure_transport_global',
649+
'failure_transport_1',
650+
'failure_transport_3',
651+
];
652+
653+
foreach ($failureTransportsMapping as $transportName => $ref) {
654+
if (\in_array($transportName, $failed_transports)) {
655+
continue;
656+
}
657+
658+
$this->assertSame('messenger.transport.'.$expectedFailureTransportsMapping[$transportName], (string) $ref, sprintf('The transport "%s" does not have the expected failed transport reference', $transportName));
659+
}
660+
661+
$failedMessageTransportListenerReference =
662+
$container->getDefinition('messenger.failure.send_failed_message_to_failure_transport_listener');
663+
$this->assertSame((string) (new Reference('messenger.transport.failure_transport_global')), (string) $failedMessageTransportListenerReference->getArgument(0));
664+
$this->assertSame($failureTransportsLocatorDefinition->getArgument(1), $failedMessageTransportListenerReference->getArgument(2));
665+
}
666+
600667
public function testMessengerTransports()
601668
{
602669
$container = $this->createContainerFromFile('messenger_transports');

src/Symfony/Component/Messenger/EventListener/SendFailedMessageToFailureTransportListener.php

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
namespace Symfony\Component\Messenger\EventListener;
1212

1313
use Psr\Log\LoggerInterface;
14+
use Symfony\Component\DependencyInjection\ServiceLocator;
1415
use Symfony\Component\ErrorHandler\Exception\FlattenException;
1516
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
1617
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
@@ -27,12 +28,17 @@
2728
*/
2829
class SendFailedMessageToFailureTransportListener implements EventSubscriberInterface
2930
{
30-
private $failureSender;
31+
private $failureSenders;
3132
private $logger;
33+
/**
34+
* @var SenderInterface|null global failure sender will be null if it is not defined.
35+
*/
36+
private $globalfailureSender;
3237

33-
public function __construct(SenderInterface $failureSender, LoggerInterface $logger = null)
38+
public function __construct(SenderInterface $globalfailureSender = null, LoggerInterface $logger = null, ServiceLocator $failureSenders = null)
3439
{
35-
$this->failureSender = $failureSender;
40+
$this->globalfailureSender = $globalfailureSender;
41+
$this->failureSenders = $failureSenders;
3642
$this->logger = $logger;
3743
}
3844

@@ -42,6 +48,11 @@ public function onMessageFailed(WorkerMessageFailedEvent $event)
4248
return;
4349
}
4450

51+
$hasFailureTransports = $this->failureSenders instanceof ServiceLocator && $this->failureSenders->has($event->getReceiverName());
52+
if ($this->globalfailureSender === null && !$hasFailureTransports) {
53+
return;
54+
}
55+
4556
$envelope = $event->getEnvelope();
4657

4758
// avoid re-sending to the failed sender
@@ -61,14 +72,15 @@ public function onMessageFailed(WorkerMessageFailedEvent $event)
6172
new RedeliveryStamp(0, $throwable->getMessage(), $flattenedException)
6273
);
6374

75+
$failureSender = $this->getFailureSender($event->getReceiverName());
6476
if (null !== $this->logger) {
6577
$this->logger->info('Rejected message {class} will be sent to the failure transport {transport}.', [
6678
'class' => \get_class($envelope->getMessage()),
67-
'transport' => \get_class($this->failureSender),
79+
'transport' => \get_class($failureSender),
6880
]);
6981
}
7082

71-
$this->failureSender->send($envelope);
83+
$failureSender->send($envelope);
7284
}
7385

7486
public static function getSubscribedEvents()
@@ -77,4 +89,13 @@ public static function getSubscribedEvents()
7789
WorkerMessageFailedEvent::class => ['onMessageFailed', -100],
7890
];
7991
}
92+
93+
private function getFailureSender(string $receiverName): SenderInterface
94+
{
95+
if ($this->failureSenders instanceof ServiceLocator && $this->failureSenders->has($receiverName)) {
96+
return $this->failureSenders->get($receiverName);
97+
}
98+
99+
return $this->globalfailureSender;
100+
}
80101
}

src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageToFailureTransportListenerTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@
2222

2323
class SendFailedMessageToFailureTransportListenerTest extends TestCase
2424
{
25+
public function testDoNothingIfFailureTransportIsNotDefined()
26+
{
27+
$sender = $this->createMock(SenderInterface::class);
28+
$sender->expects($this->never())->method('send');
29+
30+
$listener = new SendFailedMessageToFailureTransportListener(null);
31+
32+
$exception = new \Exception('no!');
33+
$envelope = new Envelope(new \stdClass());
34+
$event = new WorkerMessageFailedEvent($envelope, 'my_receiver', $exception);
35+
36+
$listener->onMessageFailed($event);
37+
}
38+
2539
public function testItSendsToTheFailureTransport()
2640
{
2741
$sender = $this->createMock(SenderInterface::class);

0 commit comments

Comments
 (0)