From a1a95266fe7e01d5ef7510e04f0df040fa72563c Mon Sep 17 00:00:00 2001 From: Peter van der Wal Date: Wed, 17 Jul 2024 21:56:24 +0200 Subject: [PATCH] [Messenger] Prioritize receivers via transport configuration --- .../Bundle/FrameworkBundle/CHANGELOG.md | 1 + .../DependencyInjection/Configuration.php | 1 + .../FrameworkExtension.php | 1 + .../Resources/config/schema/symfony-1.0.xsd | 1 + .../Fixtures/php/messenger_transports.php | 4 ++++ .../Fixtures/xml/messenger_transports.xml | 1 + .../Fixtures/yml/messenger_transports.yml | 3 +++ .../FrameworkExtensionTestCase.php | 20 +++++++++++++++++-- src/Symfony/Component/Messenger/CHANGELOG.md | 1 + .../DependencyInjection/MessengerPass.php | 10 ++++++++++ .../DependencyInjection/MessengerPassTest.php | 7 +++++-- 11 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index 7058a3fb2573f..a653e06523aa0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -9,6 +9,7 @@ CHANGELOG * Make the `config/` directory optional in `MicroKernelTrait`, add support for service arguments in the invokable Kernel class, and register `FrameworkBundle` by default when the `bundles.php` file is missing * [BC BREAK] The `secrets:decrypt-to-local` command terminates with a non-zero exit code when a secret could not be read + * Add `priority` option to the `messenger.transports` configurations and pass the configured priority to the `messenger.receiver` service tag 7.1 --- diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index d5137dc2ba805..47e03b1f4c52a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -1612,6 +1612,7 @@ function ($a) { ->defaultNull() ->info('Rate limiter name to use when processing messages') ->end() + ->integerNode('priority')->defaultValue(0)->info('Priority of this transport when the consumer is executed with the --all flag')->end() ->end() ->end() ->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index ac4c211147a7a..10fb02ec72a49 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -2220,6 +2220,7 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder ->addTag('messenger.receiver', [ 'alias' => $name, 'is_failure_transport' => \in_array($name, $failureTransports, true), + 'priority' => $transport['priority'], ]) ; $container->setDefinition($transportId = 'messenger.transport.'.$name, $transportDefinition); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index d8d23168d1887..ec11b30ff3dfa 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -606,6 +606,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transports.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transports.php index bba32ce0b9d1f..8eac58da11fbb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transports.php @@ -25,6 +25,10 @@ ], 'rate_limiter' => 'customised_worker' ], + 'prioritized' => [ + 'dsn' => 'amqp://localhost/%2f/messages?exchange_name=priority', + 'priority' => 10, + ], 'failed' => 'in-memory:///', 'redis' => 'redis://127.0.0.1:6379/messages', 'beanstalkd' => 'beanstalkd://127.0.0.1:11300', diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transports.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transports.xml index b4247360109f1..25f8dbf773ed7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transports.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transports.xml @@ -20,6 +20,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transports.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transports.yml index 6fb095a3efb1d..a32aa5c16d7fb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transports.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transports.yml @@ -23,6 +23,9 @@ framework: multiplier: 3 max_delay: 100 rate_limiter: customised_worker + prioritized: + dsn: amqp://localhost/%2f/messages?exchange_name=priority + priority: 10 failed: 'in-memory:///' redis: 'redis://127.0.0.1:6379/messages' beanstalkd: 'beanstalkd://127.0.0.1:11300' diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php index b37d2e910ec45..90e76d26cedb4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -873,6 +873,7 @@ public function testMessengerMultipleFailureTransports() $this->assertEquals([ 'alias' => 'failure_transport_1', 'is_failure_transport' => true, + 'priority' => 0, ], $failureTransport1Tags); $failureTransport3Definition = $container->getDefinition('messenger.transport.failure_transport_3'); @@ -881,6 +882,7 @@ public function testMessengerMultipleFailureTransports() $this->assertEquals([ 'alias' => 'failure_transport_3', 'is_failure_transport' => true, + 'priority' => 0, ], $failureTransport3Tags); // transport 2 exists but does not appear in the mapping @@ -913,6 +915,7 @@ public function testMessengerMultipleFailureTransportsWithGlobalFailureTransport $this->assertEquals([ 'alias' => 'failure_transport_1', 'is_failure_transport' => true, + 'priority' => 0, ], $failureTransport1Tags); $failureTransport3Definition = $container->getDefinition('messenger.transport.failure_transport_3'); @@ -921,6 +924,7 @@ public function testMessengerMultipleFailureTransportsWithGlobalFailureTransport $this->assertEquals([ 'alias' => 'failure_transport_3', 'is_failure_transport' => true, + 'priority' => 0, ], $failureTransport3Tags); $failureTransportsByTransportNameServiceLocator = $container->getDefinition('messenger.failure.send_failed_message_to_failure_transport_listener')->getArgument(0); @@ -947,8 +951,11 @@ public function testMessengerTransports() $container = $this->createContainerFromFile('messenger_transports'); $this->assertTrue($container->hasDefinition('messenger.transport.default')); $this->assertTrue($container->getDefinition('messenger.transport.default')->hasTag('messenger.receiver')); - $this->assertEquals([ - ['alias' => 'default', 'is_failure_transport' => false], ], $container->getDefinition('messenger.transport.default')->getTag('messenger.receiver')); + $this->assertEquals([[ + 'alias' => 'default', + 'is_failure_transport' => false, + 'priority' => 0, + ]], $container->getDefinition('messenger.transport.default')->getTag('messenger.receiver')); $transportArguments = $container->getDefinition('messenger.transport.default')->getArguments(); $this->assertEquals(new Reference('messenger.default_serializer'), $transportArguments[2]); @@ -956,6 +963,14 @@ public function testMessengerTransports() $transportFactory = $container->getDefinition('messenger.transport.customised')->getFactory(); $transportArguments = $container->getDefinition('messenger.transport.customised')->getArguments(); + $this->assertTrue($container->hasDefinition('messenger.transport.prioritized')); + $this->assertTrue($container->getDefinition('messenger.transport.prioritized')->hasTag('messenger.receiver')); + $this->assertEquals([[ + 'alias' => 'prioritized', + 'is_failure_transport' => false, + 'priority' => 10, + ]], $container->getDefinition('messenger.transport.prioritized')->getTag('messenger.receiver')); + $this->assertEquals([new Reference('messenger.transport_factory'), 'createTransport'], $transportFactory); $this->assertCount(3, $transportArguments); $this->assertSame('amqp://localhost/%2f/messages?exchange_name=exchange_name', $transportArguments[0]); @@ -1000,6 +1015,7 @@ public function testMessengerTransports() $failureTransportsByTransportNameServiceLocator = $container->getDefinition('messenger.failure.send_failed_message_to_failure_transport_listener')->getArgument(0); $failureTransports = $container->getDefinition((string) $failureTransportsByTransportNameServiceLocator)->getArgument(0); $expectedTransportsByFailureTransports = [ + 'prioritized' => new Reference('messenger.transport.failed'), 'beanstalkd' => new Reference('messenger.transport.failed'), 'customised' => new Reference('messenger.transport.failed'), 'default' => new Reference('messenger.transport.failed'), diff --git a/src/Symfony/Component/Messenger/CHANGELOG.md b/src/Symfony/Component/Messenger/CHANGELOG.md index e26ef63d0ab17..e8e4f897f6200 100644 --- a/src/Symfony/Component/Messenger/CHANGELOG.md +++ b/src/Symfony/Component/Messenger/CHANGELOG.md @@ -7,6 +7,7 @@ CHANGELOG * `WrappedExceptionsInterface` now extends PHP's `Throwable` interface * Add `#[AsMessage]` attribute with `$transport` parameter for message routing * Add `--format` option to the `messenger:stats` command + * Allow prioritizing receivers so that `messenger:consume --all` consumes receivers in a predefined order 7.1 --- diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index 9bb32d661ac72..67fccc3189ff0 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -242,6 +242,8 @@ private function registerReceivers(ContainerBuilder $container, array $busIds): { $receiverMapping = []; $failureTransportsMap = []; + $receiverPriority = []; + if ($container->hasDefinition('console.command.messenger_failed_messages_retry')) { $commandDefinition = $container->getDefinition('console.command.messenger_failed_messages_retry'); $globalReceiverName = $commandDefinition->getArgument(0); @@ -263,8 +265,12 @@ private function registerReceivers(ContainerBuilder $container, array $busIds): $receiverMapping[$id] = new Reference($id); foreach ($tags as $tag) { + $receiverPriority[$id] = max($tag['priority'] ?? 0, $receiverPriority[$id] ?? PHP_INT_MIN); + if (isset($tag['alias'])) { $receiverMapping[$tag['alias']] = $receiverMapping[$id]; + $receiverPriority[$tag['alias']] = max($tag['priority'] ?? 0, $receiverPriority[$tag['alias']] ?? PHP_INT_MIN); + if ($tag['is_failure_transport'] ?? false) { $failureTransportsMap[$tag['alias']] = $receiverMapping[$id]; } @@ -272,6 +278,10 @@ private function registerReceivers(ContainerBuilder $container, array $busIds): } } + $prioritySorter = fn (string $a, string $b): int => $receiverPriority[$b] <=> $receiverPriority[$a]; + uksort($receiverMapping, $prioritySorter); + uksort($failureTransportsMap, $prioritySorter); + $receiverNames = []; foreach ($receiverMapping as $name => $reference) { $receiverNames[(string) $reference] = $name; diff --git a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php index f830489634a11..51894b50aa7b5 100644 --- a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php +++ b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php @@ -24,6 +24,7 @@ use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\Messenger\Attribute\AsMessageHandler; use Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpReceiver; +use Symfony\Component\Messenger\Bridge\Doctrine\Transport\DoctrineReceiver; use Symfony\Component\Messenger\Command\ConsumeMessagesCommand; use Symfony\Component\Messenger\Command\DebugCommand; use Symfony\Component\Messenger\Command\FailedMessagesRetryCommand; @@ -448,11 +449,12 @@ public function testItRegistersMultipleReceiversAndSetsTheReceiverNamesOnTheComm ]); $container->register(AmqpReceiver::class, AmqpReceiver::class)->addTag('messenger.receiver', ['alias' => 'amqp']); + $container->register(DoctrineReceiver::class, DoctrineReceiver::class)->addTag('messenger.receiver', ['alias' => 'doctrine', 'priority' => 1]); $container->register(DummyReceiver::class, DummyReceiver::class)->addTag('messenger.receiver', ['alias' => 'dummy']); (new MessengerPass())->process($container); - $this->assertSame(['amqp', 'dummy'], $container->getDefinition('console.command.messenger_consume_messages')->getArgument(4)); + $this->assertSame(['doctrine', 'amqp', 'dummy'], $container->getDefinition('console.command.messenger_consume_messages')->getArgument(4)); } public function testItSetsTheReceiverNamesOnTheSetupTransportsCommand() @@ -464,11 +466,12 @@ public function testItSetsTheReceiverNamesOnTheSetupTransportsCommand() ]); $container->register(AmqpReceiver::class, AmqpReceiver::class)->addTag('messenger.receiver', ['alias' => 'amqp']); + $container->register(DoctrineReceiver::class, DoctrineReceiver::class)->addTag('messenger.receiver', ['alias' => 'doctrine', 'priority' => 1]); $container->register(DummyReceiver::class, DummyReceiver::class)->addTag('messenger.receiver', ['alias' => 'dummy']); (new MessengerPass())->process($container); - $this->assertSame(['amqp', 'dummy'], $container->getDefinition('console.command.messenger_setup_transports')->getArgument(1)); + $this->assertSame(['doctrine', 'amqp', 'dummy'], $container->getDefinition('console.command.messenger_setup_transports')->getArgument(1)); } public function testItRegistersHandlersOnDifferentBuses()