From 2b9ae3b20bd6494a671f4ecf3e0405e10ade5e00 Mon Sep 17 00:00:00 2001 From: Dariusz Gafka Date: Thu, 2 Jul 2026 18:12:39 +0200 Subject: [PATCH] fix: restore tenant context for error channel in multi-tenant setup --- .../MultiTenantDeadLetterTest.php | 76 +++++++++++++++++++ .../BasicMessagingModule.php | 1 + .../Handler/Gateway/ErrorChannelService.php | 15 ++-- 3 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 packages/Dbal/tests/Integration/Recoverability/MultiTenantDeadLetterTest.php diff --git a/packages/Dbal/tests/Integration/Recoverability/MultiTenantDeadLetterTest.php b/packages/Dbal/tests/Integration/Recoverability/MultiTenantDeadLetterTest.php new file mode 100644 index 000000000..9dd50aa21 --- /dev/null +++ b/packages/Dbal/tests/Integration/Recoverability/MultiTenantDeadLetterTest.php @@ -0,0 +1,76 @@ + $this->connectionForTenantA(), + 'tenant_b_connection' => $this->connectionForTenantB(), + ], + ServiceConfiguration::createWithDefaults() + ->withDefaultErrorChannel('dbal_dead_letter') + ->withExtensionObjects([ + DbalBackedMessageChannelBuilder::create('async'), + MultiTenantConfiguration::create( + 'tenant', + ['tenant_a' => 'tenant_a_connection', 'tenant_b' => 'tenant_b_connection'], + ), + ]) + ->withSkippedModulePackageNames(ModulePackageList::allPackagesExcept([ModulePackageList::ASYNCHRONOUS_PACKAGE, ModulePackageList::DBAL_PACKAGE])), + ); + + $ecotoneLite->sendCommandWithRoutingKey('order.place', 'milk', metadata: ['tenant' => 'tenant_a']); + + $ecotoneLite->run('async', ExecutionPollingMetadata::createWithTestingSetup(amountOfMessagesToHandle: 2, maxExecutionTimeInMilliseconds: 1000, failAtError: false)); + + $this->assertSame(1, $this->amountOfDeadLetterMessagesIn($this->connectionForTenantA())); + $this->assertSame(0, $this->amountOfDeadLetterMessagesIn($this->connectionForTenantB())); + } + + private function amountOfDeadLetterMessagesIn(ConnectionFactory $connectionFactory): int + { + $connection = $connectionFactory->createContext()->getDbalConnection(); + + if (! self::checkIfTableExists($connection, DbalDeadLetterHandler::DEFAULT_DEAD_LETTER_TABLE)) { + return 0; + } + + return (int) $connection + ->executeQuery(sprintf('SELECT COUNT(*) FROM %s', DbalDeadLetterHandler::DEFAULT_DEAD_LETTER_TABLE)) + ->fetchOne(); + } +} diff --git a/packages/Ecotone/src/Messaging/Config/Annotation/ModuleConfiguration/BasicMessagingModule.php b/packages/Ecotone/src/Messaging/Config/Annotation/ModuleConfiguration/BasicMessagingModule.php index 404081043..da67e1235 100644 --- a/packages/Ecotone/src/Messaging/Config/Annotation/ModuleConfiguration/BasicMessagingModule.php +++ b/packages/Ecotone/src/Messaging/Config/Annotation/ModuleConfiguration/BasicMessagingModule.php @@ -133,6 +133,7 @@ public function prepare(Configuration $messagingConfiguration, array $extensionO MediaType::parseMediaType($serviceConfiguration->getDefaultSerializationMediaType()), ]), Reference::to(ConversionService::REFERENCE_NAME), + Reference::to(MessageHeadersPropagatorInterceptor::class), ]) ); diff --git a/packages/Ecotone/src/Messaging/Handler/Gateway/ErrorChannelService.php b/packages/Ecotone/src/Messaging/Handler/Gateway/ErrorChannelService.php index 1a1e73f74..1c22f743c 100644 --- a/packages/Ecotone/src/Messaging/Handler/Gateway/ErrorChannelService.php +++ b/packages/Ecotone/src/Messaging/Handler/Gateway/ErrorChannelService.php @@ -12,6 +12,7 @@ use Ecotone\Messaging\MessageHeaders; use Ecotone\Messaging\Support\ErrorMessage; use Ecotone\Messaging\Support\MessageBuilder; +use Ecotone\Modelling\MessageHandling\MetadataPropagator\MessageHeadersPropagatorInterceptor; use Throwable; /** @@ -23,6 +24,7 @@ public function __construct( private LoggingGateway $loggingGateway, private OutboundMessageConverter $outboundMessageConverter, private ConversionService $conversionService, + private MessageHeadersPropagatorInterceptor $messageHeadersPropagator, ) { } @@ -51,11 +53,14 @@ public function handle( $messageBuilder = $messageBuilder->prependRoutingSlip([$routingSlip]); } - $errorChannel->send( - ErrorMessage::create( - $messageBuilder->build(), - $cause - ) + $this->messageHeadersPropagator->storeHeaders( + fn () => $errorChannel->send( + ErrorMessage::create( + $messageBuilder->build(), + $cause + ) + ), + $requestMessage, ); $this->loggingGateway->info(