From 3b42c3c6336563d7433972b496e2fe544016e6f5 Mon Sep 17 00:00:00 2001 From: Dariusz Gafka Date: Sun, 9 Aug 2026 08:00:00 +0200 Subject: [PATCH] refactor: split High Throughput Publishing into batching and non blocking confirmation (Enterprise) High Throughput Publishing was configured through one flag whose meaning differed per provider. Kafka, AMQP and SQS genuinely publish without blocking on confirmation, while DBAL and Redis only coalesce sends into a single blocking round trip. The flag was also named after asynchrony, which collides with the Asynchronous attribute. Configuration is now expressed as two named mechanisms under one method, and each provider only exposes the ones it can deliver. BREAKING CHANGE: withAsyncPublishing() is replaced by withHighThroughputPublishing(); MessagePublisher::asyncPublish() is replaced by publishDeferred(); the Channel\AsyncPublishing namespace is renamed to Channel\DeliveryConfirmation. publishDeferred() is no longer available on DBAL and Redis publishers, which cannot defer confirmation. --- ... => HighThroughputPublishingBenchmark.php} | 62 +++++----- .../src/AmqpBackedMessageChannelBuilder.php | 13 +- .../Amqp/src/AmqpOutboundChannelAdapter.php | 39 +++--- .../src/AmqpOutboundChannelAdapterBuilder.php | 52 ++++---- packages/Amqp/src/AmqpPendingDelivery.php | 6 +- .../AmqpMessagePublisherConfiguration.php | 35 ++++-- .../Publisher/AmqpMessagePublisherModule.php | 8 +- .../OrderWasPlaced.php | 2 +- ...ghThroughputPublishingReliabilityTest.php} | 38 +++--- ...t.php => HighThroughputPublishingTest.php} | 111 ++++++++++++++--- .../DbalMessagePublisherConfiguration.php | 14 ++- .../src/Configuration/DbalPublisherModule.php | 6 +- .../src/DbalBackedMessageChannelBuilder.php | 10 +- .../Dbal/src/DbalOutboundChannelAdapter.php | 7 +- .../src/DbalOutboundChannelAdapterBuilder.php | 18 ++- .../HighThroughputTestChannel.php} | 14 +-- .../HighThroughputTestChannelBuilder.php} | 10 +- .../OrderWasPlaced.php | 2 +- .../TestPendingDelivery.php | 8 +- ...t.php => HighThroughputPublishingTest.php} | 74 ++++++------ ...ghThroughputPublishingTransactionTest.php} | 12 +- .../AsyncPublishing/ConfirmedDelivery.php | 21 ---- ...DeferredPublishingGatewayRegistration.php} | 26 ++-- .../Config/DeliveryConfirmationModule.php} | 20 ++-- .../DeferredPublishingGateway.php} | 26 ++-- .../DeliveryConfirmationInterceptor.php} | 16 +-- .../DeliveryFuture.php | 2 +- .../DeliveryResult.php | 2 +- .../FailedDelivery.php | 2 +- .../PendingDelivery.php | 2 +- .../PendingDeliveryRegistry.php} | 4 +- .../PublishingFailedException.php | 6 +- .../SendRetryChannelInterceptor.php | 4 +- .../RegisterSingletonMessagingServices.php | 4 +- .../src/Messaging/Config/ModuleClassList.php | 4 +- .../src/Messaging/MessagePublisher.php | 7 +- packages/Ecotone/src/Messaging/Precedence.php | 4 +- .../AsyncOrderForwarder.php | 2 +- .../AsyncOrderSubscriber.php | 2 +- .../FakeTransactionInterceptor.php | 2 +- .../FakeTransactionModule.php | 2 +- ...InMemoryHighThroughputOutboundAdapter.php} | 10 +- ...InMemoryHighThroughputPublisherModule.php} | 12 +- ...MemoryHighThroughputPublishingChannel.php} | 14 +-- ...ighThroughputPublishingChannelBuilder.php} | 10 +- .../InMemoryPendingDelivery.php | 8 +- .../OperationsLog.php | 2 +- .../OrderRequestReceived.php | 2 +- .../OrderService.php | 2 +- .../OrderWasPlaced.php | 2 +- .../MessagePublisherAsyncPublishTest.php | 112 ------------------ .../DeadLetterOfFailedBatchDeliveriesTest.php | 14 +-- .../HighThroughputPublishingChannelTest.php} | 18 +-- ...roughputPublishingCollectorMatrixTest.php} | 18 +-- ...ghThroughputPublishingReliabilityTest.php} | 74 ++++++------ ...HighThroughputPublishingScenariosTest.php} | 26 ++-- ...sagePublisherPublishDeferredBatchTest.php} | 22 ++-- .../MessagePublisherPublishDeferredTest.php | 112 ++++++++++++++++++ .../SendRetryOfFailedBatchDeliveriesTest.php | 6 +- .../tests/Messaging/Unit/PrecedenceTest.php | 4 +- .../src/EnqueueOutboundChannelAdapter.php | 22 +--- .../Kafka/src/Channel/KafkaMessageChannel.php | 2 +- .../Channel/KafkaMessageChannelBuilder.php | 34 ++++-- .../Kafka/src/Configuration/KafkaModule.php | 6 +- .../KafkaPublisherConfiguration.php | 43 ++++--- .../src/Outbound/KafkaDeliveryTracker.php | 4 +- .../Outbound/KafkaOutboundChannelAdapter.php | 37 +++--- .../KafkaOutboundChannelAdapterBuilder.php | 4 +- .../src/Outbound/KafkaPendingDelivery.php | 4 +- ...ghThroughputPublishingReliabilityTest.php} | 8 +- ...t.php => HighThroughputPublishingTest.php} | 28 ++--- .../RedisMessagePublisherConfiguration.php | 14 ++- .../RedisMessagePublisherModule.php | 6 +- .../src/RedisBackedMessageChannelBuilder.php | 10 +- .../Redis/src/RedisOutboundChannelAdapter.php | 11 +- .../RedisOutboundChannelAdapterBuilder.php | 18 ++- .../OrderWasPlaced.php | 2 +- ...ghThroughputPublishingReliabilityTest.php} | 4 +- ...t.php => HighThroughputPublishingTest.php} | 68 +++++------ .../SqsMessagePublisherConfiguration.php | 34 ++++-- .../SqsMessagePublisherModule.php | 6 +- .../src/SqsBackedMessageChannelBuilder.php | 11 +- .../Sqs/src/SqsOutboundChannelAdapter.php | 24 ++-- .../src/SqsOutboundChannelAdapterBuilder.php | 40 ++++--- packages/Sqs/src/SqsPendingDelivery.php | 6 +- .../OrderWasPlaced.php | 2 +- ...ghThroughputPublishingReliabilityTest.php} | 16 +-- ...t.php => HighThroughputPublishingTest.php} | 44 +++---- 88 files changed, 896 insertions(+), 779 deletions(-) rename Monorepo/Benchmark/{AsyncPublishingBenchmark.php => HighThroughputPublishingBenchmark.php} (88%) rename packages/Amqp/tests/Fixture/{AsyncPublishing => HighThroughputPublishing}/OrderWasPlaced.php (72%) rename packages/Amqp/tests/Integration/{AsyncPublishingReliabilityTest.php => HighThroughputPublishingReliabilityTest.php} (85%) rename packages/Amqp/tests/Integration/{AsyncPublishingTest.php => HighThroughputPublishingTest.php} (71%) rename packages/Dbal/tests/Fixture/{AsyncPublishing/AsyncPublishingTestChannel.php => HighThroughputPublishing/HighThroughputTestChannel.php} (77%) rename packages/Dbal/tests/Fixture/{AsyncPublishing/AsyncPublishingTestChannelBuilder.php => HighThroughputPublishing/HighThroughputTestChannelBuilder.php} (74%) rename packages/{Redis/tests/Fixture/AsyncPublishing => Dbal/tests/Fixture/HighThroughputPublishing}/OrderWasPlaced.php (72%) rename packages/Dbal/tests/Fixture/{AsyncPublishing => HighThroughputPublishing}/TestPendingDelivery.php (75%) rename packages/Dbal/tests/Integration/{AsyncPublishingTest.php => HighThroughputPublishingTest.php} (81%) rename packages/Dbal/tests/Integration/{AsyncPublishingTransactionTest.php => HighThroughputPublishingTransactionTest.php} (87%) delete mode 100644 packages/Ecotone/src/Messaging/Channel/AsyncPublishing/ConfirmedDelivery.php rename packages/Ecotone/src/Messaging/Channel/{AsyncPublishing/Config/AsyncPublishGatewayRegistration.php => DeliveryConfirmation/Config/DeferredPublishingGatewayRegistration.php} (64%) rename packages/Ecotone/src/Messaging/Channel/{AsyncPublishing/Config/AsyncPublishingModule.php => DeliveryConfirmation/Config/DeliveryConfirmationModule.php} (80%) rename packages/Ecotone/src/Messaging/Channel/{AsyncPublishing/AsyncPublishingGateway.php => DeliveryConfirmation/DeferredPublishingGateway.php} (68%) rename packages/Ecotone/src/Messaging/Channel/{AsyncPublishing/AsyncPublishingWaiterInterceptor.php => DeliveryConfirmation/DeliveryConfirmationInterceptor.php} (86%) rename packages/Ecotone/src/Messaging/Channel/{AsyncPublishing => DeliveryConfirmation}/DeliveryFuture.php (97%) rename packages/Ecotone/src/Messaging/Channel/{AsyncPublishing => DeliveryConfirmation}/DeliveryResult.php (93%) rename packages/Ecotone/src/Messaging/Channel/{AsyncPublishing => DeliveryConfirmation}/FailedDelivery.php (90%) rename packages/Ecotone/src/Messaging/Channel/{AsyncPublishing => DeliveryConfirmation}/PendingDelivery.php (76%) rename packages/Ecotone/src/Messaging/Channel/{AsyncPublishing/AsyncPublishingRegistry.php => DeliveryConfirmation/PendingDeliveryRegistry.php} (98%) rename packages/Ecotone/src/Messaging/Channel/{AsyncPublishing => DeliveryConfirmation}/PublishingFailedException.php (76%) rename packages/Ecotone/tests/Messaging/Fixture/{AsyncPublishing => HighThroughputPublishing}/AsyncOrderForwarder.php (90%) rename packages/Ecotone/tests/Messaging/Fixture/{AsyncPublishing => HighThroughputPublishing}/AsyncOrderSubscriber.php (90%) rename packages/Ecotone/tests/Messaging/Fixture/{AsyncPublishing => HighThroughputPublishing}/FakeTransactionInterceptor.php (93%) rename packages/Ecotone/tests/Messaging/Fixture/{AsyncPublishing => HighThroughputPublishing}/FakeTransactionModule.php (97%) rename packages/Ecotone/tests/Messaging/Fixture/{AsyncPublishing/InMemoryAsyncOutboundAdapter.php => HighThroughputPublishing/InMemoryHighThroughputOutboundAdapter.php} (88%) rename packages/Ecotone/tests/Messaging/Fixture/{AsyncPublishing/InMemoryAsyncPublisherModule.php => HighThroughputPublishing/InMemoryHighThroughputPublisherModule.php} (74%) rename packages/Ecotone/tests/Messaging/Fixture/{AsyncPublishing/InMemoryAsyncPublishingChannel.php => HighThroughputPublishing/InMemoryHighThroughputPublishingChannel.php} (88%) rename packages/Ecotone/tests/Messaging/Fixture/{AsyncPublishing/InMemoryAsyncPublishingChannelBuilder.php => HighThroughputPublishing/InMemoryHighThroughputPublishingChannelBuilder.php} (71%) rename packages/Ecotone/tests/Messaging/Fixture/{AsyncPublishing => HighThroughputPublishing}/InMemoryPendingDelivery.php (86%) rename packages/Ecotone/tests/Messaging/Fixture/{AsyncPublishing => HighThroughputPublishing}/OperationsLog.php (85%) rename packages/Ecotone/tests/Messaging/Fixture/{AsyncPublishing => HighThroughputPublishing}/OrderRequestReceived.php (71%) rename packages/Ecotone/tests/Messaging/Fixture/{AsyncPublishing => HighThroughputPublishing}/OrderService.php (92%) rename packages/Ecotone/tests/Messaging/Fixture/{AsyncPublishing => HighThroughputPublishing}/OrderWasPlaced.php (70%) delete mode 100644 packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/MessagePublisherAsyncPublishTest.php rename packages/Ecotone/tests/Messaging/Unit/Channel/{AsyncPublishing => DeliveryConfirmation}/DeadLetterOfFailedBatchDeliveriesTest.php (88%) rename packages/Ecotone/tests/Messaging/Unit/Channel/{AsyncPublishing/AsyncPublishingChannelTest.php => DeliveryConfirmation/HighThroughputPublishingChannelTest.php} (78%) rename packages/Ecotone/tests/Messaging/Unit/Channel/{AsyncPublishing/AsyncPublishingCollectorMatrixTest.php => DeliveryConfirmation/HighThroughputPublishingCollectorMatrixTest.php} (77%) rename packages/Ecotone/tests/Messaging/Unit/Channel/{AsyncPublishing/AsyncPublishingReliabilityTest.php => DeliveryConfirmation/HighThroughputPublishingReliabilityTest.php} (71%) rename packages/Ecotone/tests/Messaging/Unit/Channel/{AsyncPublishing/AsyncPublishingScenariosTest.php => DeliveryConfirmation/HighThroughputPublishingScenariosTest.php} (87%) rename packages/Ecotone/tests/Messaging/Unit/Channel/{AsyncPublishing/MessagePublisherAsyncPublishBatchTest.php => DeliveryConfirmation/MessagePublisherPublishDeferredBatchTest.php} (63%) create mode 100644 packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/MessagePublisherPublishDeferredTest.php rename packages/Ecotone/tests/Messaging/Unit/Channel/{AsyncPublishing => DeliveryConfirmation}/SendRetryOfFailedBatchDeliveriesTest.php (94%) rename packages/Kafka/tests/Integration/{AsyncPublishingReliabilityTest.php => HighThroughputPublishingReliabilityTest.php} (90%) rename packages/Kafka/tests/Integration/{AsyncPublishingTest.php => HighThroughputPublishingTest.php} (91%) rename packages/{Sqs/tests/Fixture/AsyncPublishing => Redis/tests/Fixture/HighThroughputPublishing}/OrderWasPlaced.php (71%) rename packages/Redis/tests/Integration/{AsyncPublishingReliabilityTest.php => HighThroughputPublishingReliabilityTest.php} (95%) rename packages/Redis/tests/Integration/{AsyncPublishingTest.php => HighThroughputPublishingTest.php} (83%) rename packages/{Dbal/tests/Fixture/AsyncPublishing => Sqs/tests/Fixture/HighThroughputPublishing}/OrderWasPlaced.php (72%) rename packages/Sqs/tests/Integration/{AsyncPublishingReliabilityTest.php => HighThroughputPublishingReliabilityTest.php} (84%) rename packages/Sqs/tests/Integration/{AsyncPublishingTest.php => HighThroughputPublishingTest.php} (85%) diff --git a/Monorepo/Benchmark/AsyncPublishingBenchmark.php b/Monorepo/Benchmark/HighThroughputPublishingBenchmark.php similarity index 88% rename from Monorepo/Benchmark/AsyncPublishingBenchmark.php rename to Monorepo/Benchmark/HighThroughputPublishingBenchmark.php index 4814fc83b..2ad81cea6 100644 --- a/Monorepo/Benchmark/AsyncPublishingBenchmark.php +++ b/Monorepo/Benchmark/HighThroughputPublishingBenchmark.php @@ -42,7 +42,7 @@ * and command reply respectively), so the asynchronous scenarios are not supported for them and are not benchmarked. */ #[Warmup(0), Revs(1), Iterations(10)] -class AsyncPublishingBenchmark +class HighThroughputPublishingBenchmark { private const AMOUNT_OF_PUBLISHED_MESSAGES = 1000; @@ -58,13 +58,13 @@ class AsyncPublishingBenchmark public function setUpAmqpSynchronousPublishing(): void { - $this->publisher = $this->bootstrapAmqpPublisher(asyncPublishing: false); + $this->publisher = $this->bootstrapAmqpPublisher(highThroughputPublishing: false); $this->warmUpPublisher(); } - public function setUpAmqpAsyncPublishing(): void + public function setUpAmqpHighThroughputPublishing(): void { - $this->publisher = $this->bootstrapAmqpPublisher(asyncPublishing: true); + $this->publisher = $this->bootstrapAmqpPublisher(highThroughputPublishing: true); $this->warmUpPublisher(); } @@ -80,13 +80,13 @@ public function setUpAmqpBatchChannel(): void public function setUpKafkaSynchronousPublishing(): void { - $this->publisher = $this->bootstrapKafkaPublisher(asyncPublishing: false); + $this->publisher = $this->bootstrapKafkaPublisher(highThroughputPublishing: false); $this->warmUpPublisher(); } - public function setUpKafkaAsyncPublishing(): void + public function setUpKafkaHighThroughputPublishing(): void { - $this->publisher = $this->bootstrapKafkaPublisher(asyncPublishing: true); + $this->publisher = $this->bootstrapKafkaPublisher(highThroughputPublishing: true); $this->warmUpPublisher(); } @@ -135,13 +135,13 @@ public function setUpRedisBatchChannel(): void public function setUpSqsSynchronousPublishing(): void { - $this->publisher = $this->bootstrapSqsPublisher(asyncPublishing: false); + $this->publisher = $this->bootstrapSqsPublisher(highThroughputPublishing: false); $this->warmUpPublisher(); } - public function setUpSqsAsyncPublishing(): void + public function setUpSqsHighThroughputPublishing(): void { - $this->publisher = $this->bootstrapSqsPublisher(asyncPublishing: true); + $this->publisher = $this->bootstrapSqsPublisher(highThroughputPublishing: true); $this->warmUpPublisher(); } @@ -161,7 +161,7 @@ public function bench_amqp_single_message_synchronous(): void $this->publishSynchronouslyOneByOne(); } - #[BeforeMethods('setUpAmqpAsyncPublishing')] + #[BeforeMethods('setUpAmqpHighThroughputPublishing')] public function bench_amqp_single_message_asynchronous(): void { $this->publishAsynchronouslyOneByOne(); @@ -173,7 +173,7 @@ public function bench_amqp_batch_message_synchronous(): void $this->publishBatchSynchronously(); } - #[BeforeMethods('setUpAmqpAsyncPublishing')] + #[BeforeMethods('setUpAmqpHighThroughputPublishing')] public function bench_amqp_batch_message_asynchronous(): void { $this->publishBatchAsynchronously(); @@ -185,7 +185,7 @@ public function bench_amqp_multiple_batches_synchronous(): void $this->publishMultipleBatchesSynchronously(); } - #[BeforeMethods('setUpAmqpAsyncPublishing')] + #[BeforeMethods('setUpAmqpHighThroughputPublishing')] public function bench_amqp_multiple_batches_asynchronous(): void { $this->publishMultipleBatchesAsynchronously(); @@ -197,7 +197,7 @@ public function bench_kafka_single_message_synchronous(): void $this->publishSynchronouslyOneByOne(); } - #[BeforeMethods('setUpKafkaAsyncPublishing')] + #[BeforeMethods('setUpKafkaHighThroughputPublishing')] public function bench_kafka_single_message_asynchronous(): void { $this->publishAsynchronouslyOneByOne(); @@ -209,7 +209,7 @@ public function bench_kafka_batch_message_synchronous(): void $this->publishBatchSynchronously(); } - #[BeforeMethods('setUpKafkaAsyncPublishing')] + #[BeforeMethods('setUpKafkaHighThroughputPublishing')] public function bench_kafka_batch_message_asynchronous(): void { $this->publishBatchAsynchronously(); @@ -221,7 +221,7 @@ public function bench_kafka_multiple_batches_synchronous(): void $this->publishMultipleBatchesSynchronously(); } - #[BeforeMethods('setUpKafkaAsyncPublishing')] + #[BeforeMethods('setUpKafkaHighThroughputPublishing')] public function bench_kafka_multiple_batches_asynchronous(): void { $this->publishMultipleBatchesAsynchronously(); @@ -257,7 +257,7 @@ public function bench_sqs_single_message_synchronous(): void $this->publishSynchronouslyOneByOne(); } - #[BeforeMethods('setUpSqsAsyncPublishing')] + #[BeforeMethods('setUpSqsHighThroughputPublishing')] public function bench_sqs_single_message_asynchronous(): void { $this->publishAsynchronouslyOneByOne(); @@ -269,7 +269,7 @@ public function bench_sqs_batch_message_synchronous(): void $this->publishBatchSynchronously(); } - #[BeforeMethods('setUpSqsAsyncPublishing')] + #[BeforeMethods('setUpSqsHighThroughputPublishing')] public function bench_sqs_batch_message_asynchronous(): void { $this->publishBatchAsynchronously(); @@ -281,7 +281,7 @@ public function bench_sqs_multiple_batches_synchronous(): void $this->publishMultipleBatchesSynchronously(); } - #[BeforeMethods('setUpSqsAsyncPublishing')] + #[BeforeMethods('setUpSqsHighThroughputPublishing')] public function bench_sqs_multiple_batches_asynchronous(): void { $this->publishMultipleBatchesAsynchronously(); @@ -298,7 +298,7 @@ private function publishAsynchronouslyOneByOne(): void { $futures = []; for ($messageNumber = 0; $messageNumber < self::AMOUNT_OF_PUBLISHED_MESSAGES; $messageNumber++) { - $futures[] = $this->publisher->asyncPublish(self::MESSAGE_PAYLOAD, MediaType::TEXT_PLAIN); + $futures[] = $this->publisher->publishDeferred(self::MESSAGE_PAYLOAD, MediaType::TEXT_PLAIN); } foreach ($futures as $future) { $future->resolve(); @@ -314,7 +314,7 @@ private function publishBatchSynchronously(): void private function publishBatchAsynchronously(): void { - $this->publisher->asyncPublish($this->buildBatch(self::AMOUNT_OF_PUBLISHED_MESSAGES), MediaType::TEXT_PLAIN)->resolve(); + $this->publisher->publishDeferred($this->buildBatch(self::AMOUNT_OF_PUBLISHED_MESSAGES), MediaType::TEXT_PLAIN)->resolve(); } private function publishMultipleBatchesSynchronously(): void @@ -330,7 +330,7 @@ private function publishMultipleBatchesAsynchronously(): void { $futures = []; for ($batchNumber = 0; $batchNumber < self::AMOUNT_OF_BATCHES; $batchNumber++) { - $futures[] = $this->publisher->asyncPublish($this->buildBatch(self::MESSAGES_PER_BATCH), MediaType::TEXT_PLAIN); + $futures[] = $this->publisher->publishDeferred($this->buildBatch(self::MESSAGES_PER_BATCH), MediaType::TEXT_PLAIN); } foreach ($futures as $future) { $future->resolve(); @@ -375,7 +375,7 @@ private function bootstrapBatchChannel(string $modulePackage, object $channelBui return $messaging->getMessageChannel($channelBuilder->getMessageChannelName()); } - private function bootstrapAmqpPublisher(bool $asyncPublishing): MessagePublisher + private function bootstrapAmqpPublisher(bool $highThroughputPublishing): MessagePublisher { $queueName = uniqid('benchmark_orders_'); $connectionFactory = new AmqpConnectionFactory(['dsn' => getenv('RABBIT_HOST') ?: 'amqp://guest:guest@localhost:5672/%2f']); @@ -385,8 +385,8 @@ private function bootstrapAmqpPublisher(bool $asyncPublishing): MessagePublisher $publisherConfiguration = AmqpMessagePublisherConfiguration::create() ->withAutoDeclareQueueOnSend(true) ->withDefaultRoutingKey($queueName); - if ($asyncPublishing) { - $publisherConfiguration = $publisherConfiguration->withAsyncPublishing(); + if ($highThroughputPublishing) { + $publisherConfiguration = $publisherConfiguration->withHighThroughputPublishing(); } $messaging = EcotoneLite::bootstrapFlowTesting( @@ -435,11 +435,11 @@ private function bootstrapRedisPublisher(): MessagePublisher return $messaging->getGateway(MessagePublisher::class); } - private function bootstrapSqsPublisher(bool $asyncPublishing): MessagePublisher + private function bootstrapSqsPublisher(bool $highThroughputPublishing): MessagePublisher { $publisherConfiguration = SqsMessagePublisherConfiguration::create(queueName: uniqid('benchmark_orders_')); - if ($asyncPublishing) { - $publisherConfiguration = $publisherConfiguration->withAsyncPublishing(); + if ($highThroughputPublishing) { + $publisherConfiguration = $publisherConfiguration->withHighThroughputPublishing(); } $messaging = EcotoneLite::bootstrapFlowTesting( @@ -456,11 +456,11 @@ private function bootstrapSqsPublisher(bool $asyncPublishing): MessagePublisher return $messaging->getGateway(MessagePublisher::class); } - private function bootstrapKafkaPublisher(bool $asyncPublishing): MessagePublisher + private function bootstrapKafkaPublisher(bool $highThroughputPublishing): MessagePublisher { $publisherConfiguration = KafkaPublisherConfiguration::createWithDefaults(topicName: uniqid('benchmark_orders_')); - if ($asyncPublishing) { - $publisherConfiguration = $publisherConfiguration->withAsyncPublishing(); + if ($highThroughputPublishing) { + $publisherConfiguration = $publisherConfiguration->withHighThroughputPublishing(); } $messaging = EcotoneLite::bootstrapFlowTesting( diff --git a/packages/Amqp/src/AmqpBackedMessageChannelBuilder.php b/packages/Amqp/src/AmqpBackedMessageChannelBuilder.php index cfa770b15..7762cabef 100644 --- a/packages/Amqp/src/AmqpBackedMessageChannelBuilder.php +++ b/packages/Amqp/src/AmqpBackedMessageChannelBuilder.php @@ -30,7 +30,7 @@ private function __construct( ->withDefaultRoutingKey($queueName) ->withAutoDeclareOnSend(true) ->withDefaultPersistentMode(true) - ->withAsyncPublishingChannelName($channelName) + ->withPublishingChannelName($channelName) ); } @@ -72,9 +72,14 @@ public function withPublisherConfirms(bool $enabled): self return $this; } - public function withHighThroughputPublishing(bool $enabled = true, ?int $timeoutInMilliseconds = null): self + /** + * @param bool $batchPublishing coalesces published Messages into a single publisher confirms round trip + * @param bool $nonBlockingConfirmation publishes without waiting for publisher confirms, which are awaited before the surrounding Command Bus or asynchronous endpoint finishes + * @param int|null $confirmationTimeoutInMilliseconds how long to await publisher confirms before treating the delivery as failed + */ + public function withHighThroughputPublishing(bool $batchPublishing = true, bool $nonBlockingConfirmation = true, ?int $confirmationTimeoutInMilliseconds = null): self { - $this->getAmqpOutboundChannelAdapter()->withAsyncPublishing($enabled, $timeoutInMilliseconds); + $this->getAmqpOutboundChannelAdapter()->withHighThroughputPublishing($batchPublishing, $nonBlockingConfirmation, $confirmationTimeoutInMilliseconds); return $this; } @@ -93,7 +98,7 @@ public function getMessageChannelName(): string protected function supportsBatchMessages(): bool { - return $this->getAmqpOutboundChannelAdapter()->isAsyncPublishingEnabled(); + return $this->getAmqpOutboundChannelAdapter()->isBatchPublishingEnabled(); } public function getQueueName() diff --git a/packages/Amqp/src/AmqpOutboundChannelAdapter.php b/packages/Amqp/src/AmqpOutboundChannelAdapter.php index 5bbb01105..92ab7b35f 100644 --- a/packages/Amqp/src/AmqpOutboundChannelAdapter.php +++ b/packages/Amqp/src/AmqpOutboundChannelAdapter.php @@ -7,9 +7,9 @@ use Ecotone\Amqp\Transaction\AmqpTransactionInterceptor; use Ecotone\Enqueue\CachedConnectionFactory; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; -use Ecotone\Messaging\Channel\AsyncPublishing\FailedDelivery; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; +use Ecotone\Messaging\Channel\DeliveryConfirmation\FailedDelivery; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDeliveryRegistry; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Channel\PollableChannel\Serialization\OutboundMessageConverter; use Ecotone\Messaging\Config\ConfigurationException; use Ecotone\Messaging\Conversion\ConversionService; @@ -54,10 +54,11 @@ public function __construct( private OutboundMessageConverter $outboundMessageConverter, private ConversionService $conversionService, private AmqpTransactionInterceptor $amqpTransactionInterceptor, - private AsyncPublishingRegistry $asyncPublishingRegistry, + private PendingDeliveryRegistry $pendingDeliveryRegistry, private ?DelayStrategy $delayStrategy = null, - private bool $asyncPublishing = false, - private int $asyncPublishingTimeout = AmqpOutboundChannelAdapterBuilder::DEFAULT_ASYNC_PUBLISHING_TIMEOUT, + private bool $batchPublishing = false, + private bool $nonBlockingConfirmation = false, + private int $confirmationTimeout = AmqpOutboundChannelAdapterBuilder::DEFAULT_CONFIRMATION_TIMEOUT, private string $channelName = '', ) { } @@ -68,8 +69,8 @@ public function __construct( public function handle(Message $message): void { $payload = $message->getPayload(); - if ($payload instanceof BatchMessage && ! $this->asyncPublishing) { - throw ConfigurationException::create(sprintf('Sending BatchMessage over `%s` requires async publishing to be enabled. Enable it with withAsyncPublishing(), available as part of Ecotone Enterprise.', $this->channelName !== '' ? $this->channelName : $this->exchangeName)); + if ($payload instanceof BatchMessage && ! $this->batchPublishing) { + throw ConfigurationException::create(sprintf('Sending BatchMessage over `%s` requires batch publishing to be enabled. Enable it with withHighThroughputPublishing(), available as part of Ecotone Enterprise.', $this->channelName !== '' ? $this->channelName : $this->exchangeName)); } $messagesToPublish = $payload instanceof BatchMessage @@ -89,7 +90,7 @@ public function handle(Message $message): void $publishRecords = $this->publishMessages($messagesToPublish, $context, $confirmations); - if ($publishRecords !== [] && $confirmations !== null && $this->canPublishAsynchronously()) { + if ($publishRecords !== [] && $confirmations !== null && $this->canDeferConfirmation()) { $this->registerPendingDelivery($publishRecords, $context, $confirmations, $prePublishConfirmationsEpoch); return; @@ -98,9 +99,9 @@ public function handle(Message $message): void $this->awaitPublisherConfirmsSynchronously($publishRecords, $context, $confirmations, $prePublishConfirmationsEpoch); } - public function isAsyncPublishingEnabled(): bool + public function isNonBlockingConfirmationEnabled(): bool { - return $this->asyncPublishing; + return $this->nonBlockingConfirmation; } /** @@ -267,11 +268,11 @@ private function prepareInteropMessage(Message $message): array return [$messageToSend, $exchangeName, $outboundMessage->getDeliveryDelay(), $timeToLive]; } - private function canPublishAsynchronously(): bool + private function canDeferConfirmation(): bool { - return $this->asyncPublishing + return $this->nonBlockingConfirmation && $this->publisherConfirms - && $this->asyncPublishingRegistry->isScopeActive(); + && $this->pendingDeliveryRegistry->isScopeActive(); } /** @@ -279,12 +280,12 @@ private function canPublishAsynchronously(): bool */ private function registerPendingDelivery(array $publishRecords, InteropAmqpContext $context, AmqpPublisherConfirmations $confirmations, int $prePublishConfirmationsEpoch): void { - $this->asyncPublishingRegistry->register( + $this->pendingDeliveryRegistry->register( $this->channelName, new AmqpPendingDelivery( $context, $publishRecords, - $this->asyncPublishingTimeout, + $this->confirmationTimeout, $this->channelName, $confirmations, $prePublishConfirmationsEpoch, @@ -302,7 +303,7 @@ private function awaitPublisherConfirmsSynchronously(array $publishRecords, Inte } if ($publishRecords === [] || $confirmations === null) { - $timeoutInSeconds = $this->asyncPublishingTimeout / 1000; + $timeoutInSeconds = $this->confirmationTimeout / 1000; if ($context instanceof AmqpLibContext) { $context->getLibChannel()->wait_for_pending_acks_returns($timeoutInSeconds); } elseif ($context instanceof AmqpExtContext) { @@ -315,7 +316,7 @@ private function awaitPublisherConfirmsSynchronously(array $publishRecords, Inte $deliveryResult = (new AmqpPendingDelivery( $context, $publishRecords, - $this->asyncPublishingTimeout, + $this->confirmationTimeout, $this->channelName, $confirmations, $prePublishConfirmationsEpoch, @@ -325,7 +326,7 @@ private function awaitPublisherConfirmsSynchronously(array $publishRecords, Inte return; } - if ($this->asyncPublishing) { + if ($this->nonBlockingConfirmation) { throw PublishingFailedException::withFailedDeliveries($deliveryResult->getFailedDeliveries()); } diff --git a/packages/Amqp/src/AmqpOutboundChannelAdapterBuilder.php b/packages/Amqp/src/AmqpOutboundChannelAdapterBuilder.php index 002b5a577..696ec8d14 100644 --- a/packages/Amqp/src/AmqpOutboundChannelAdapterBuilder.php +++ b/packages/Amqp/src/AmqpOutboundChannelAdapterBuilder.php @@ -7,7 +7,7 @@ use Ecotone\Amqp\Transaction\AmqpTransactionInterceptor; use Ecotone\Enqueue\CachedConnectionFactory; use Ecotone\Enqueue\EnqueueOutboundChannelAdapterBuilder; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDeliveryRegistry; use Ecotone\Messaging\Channel\PollableChannel\Serialization\OutboundMessageConverter; use Ecotone\Messaging\Config\Container\Definition; use Ecotone\Messaging\Config\Container\MessagingContainerBuilder; @@ -23,7 +23,7 @@ class AmqpOutboundChannelAdapterBuilder extends EnqueueOutboundChannelAdapterBui { private const DEFAULT_PERSISTENT_MODE = true; - public const DEFAULT_ASYNC_PUBLISHING_TIMEOUT = 12000; + public const DEFAULT_CONFIRMATION_TIMEOUT = 12000; private string $amqpConnectionFactoryReferenceName; private string $defaultRoutingKey = ''; @@ -34,9 +34,10 @@ class AmqpOutboundChannelAdapterBuilder extends EnqueueOutboundChannelAdapterBui private array $staticHeadersToAdd = []; private bool $publisherConfirms = true; private ?string $delayStrategyReferenceName = null; - private bool $asyncPublishing = false; - private int $asyncPublishingTimeout = self::DEFAULT_ASYNC_PUBLISHING_TIMEOUT; - private ?string $asyncPublishingChannelName = null; + private bool $batchPublishing = false; + private bool $nonBlockingConfirmation = false; + private int $confirmationTimeout = self::DEFAULT_CONFIRMATION_TIMEOUT; + private ?string $publishingChannelName = null; private function __construct(string $exchangeName, string $amqpConnectionFactoryReferenceName) { @@ -74,25 +75,31 @@ public function withPublisherConfirms(bool $publisherConfirms): self return $this; } - public function withAsyncPublishing(bool $enabled = true, ?int $timeoutInMilliseconds = null): self + public function withHighThroughputPublishing(bool $batchPublishing = true, bool $nonBlockingConfirmation = true, ?int $confirmationTimeoutInMilliseconds = null): self { - Assert::isTrue($timeoutInMilliseconds === null || $timeoutInMilliseconds > 0, 'Async publishing timeout must be a positive amount of milliseconds.'); - $this->asyncPublishing = $enabled; - if ($timeoutInMilliseconds !== null) { - $this->asyncPublishingTimeout = $timeoutInMilliseconds; + Assert::isTrue($confirmationTimeoutInMilliseconds === null || $confirmationTimeoutInMilliseconds > 0, 'Confirmation timeout must be a positive amount of milliseconds.'); + $this->batchPublishing = $batchPublishing; + $this->nonBlockingConfirmation = $nonBlockingConfirmation; + if ($confirmationTimeoutInMilliseconds !== null) { + $this->confirmationTimeout = $confirmationTimeoutInMilliseconds; } return $this; } - public function isAsyncPublishingEnabled(): bool + public function isBatchPublishingEnabled(): bool { - return $this->asyncPublishing; + return $this->batchPublishing; } - public function withAsyncPublishingChannelName(string $channelName): self + public function isNonBlockingConfirmationEnabled(): bool { - $this->asyncPublishingChannelName = $channelName; + return $this->nonBlockingConfirmation; + } + + public function withPublishingChannelName(string $channelName): self + { + $this->publishingChannelName = $channelName; return $this; } @@ -149,11 +156,13 @@ public function withDefaultPersistentMode(bool $isPersistent): self public function compile(MessagingContainerBuilder $builder): Definition { - if ($this->asyncPublishing) { + if ($this->batchPublishing || $this->nonBlockingConfirmation) { if (! $builder->getServiceConfiguration()->isRunningForEnterprise()) { - throw LicensingException::create('Asynchronous publishing is available only with Ecotone Enterprise licence.'); + throw LicensingException::create('High Throughput Publishing is available only with Ecotone Enterprise licence.'); } - Assert::isTrue($this->publisherConfirms, 'Asynchronous publishing requires publisher confirms to be enabled.'); + } + if ($this->nonBlockingConfirmation) { + Assert::isTrue($this->publisherConfirms, 'Non blocking confirmation requires publisher confirms to be enabled.'); } $connectionFactory = new Definition(CachedConnectionFactory::class, [ @@ -186,11 +195,12 @@ public function compile(MessagingContainerBuilder $builder): Definition $outboundMessageConverter, new Reference(ConversionService::REFERENCE_NAME), Reference::to(AmqpTransactionInterceptor::class), - new Reference(AsyncPublishingRegistry::class), + new Reference(PendingDeliveryRegistry::class), $this->delayStrategyReferenceName ? new Reference($this->delayStrategyReferenceName) : null, - $this->asyncPublishing, - $this->asyncPublishingTimeout, - $this->asyncPublishingChannelName ?? $this->exchangeName, + $this->batchPublishing, + $this->nonBlockingConfirmation, + $this->confirmationTimeout, + $this->publishingChannelName ?? $this->exchangeName, ]); } } diff --git a/packages/Amqp/src/AmqpPendingDelivery.php b/packages/Amqp/src/AmqpPendingDelivery.php index 4c010a9a0..f53de7f92 100644 --- a/packages/Amqp/src/AmqpPendingDelivery.php +++ b/packages/Amqp/src/AmqpPendingDelivery.php @@ -4,9 +4,9 @@ namespace Ecotone\Amqp; -use Ecotone\Messaging\Channel\AsyncPublishing\DeliveryResult; -use Ecotone\Messaging\Channel\AsyncPublishing\FailedDelivery; -use Ecotone\Messaging\Channel\AsyncPublishing\PendingDelivery; +use Ecotone\Messaging\Channel\DeliveryConfirmation\DeliveryResult; +use Ecotone\Messaging\Channel\DeliveryConfirmation\FailedDelivery; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDelivery; use Ecotone\Messaging\Message; use Enqueue\AmqpExt\AmqpContext as AmqpExtContext; use Enqueue\AmqpLib\AmqpContext as AmqpLibContext; diff --git a/packages/Amqp/src/Publisher/AmqpMessagePublisherConfiguration.php b/packages/Amqp/src/Publisher/AmqpMessagePublisherConfiguration.php index e3fa17a60..3588fcb17 100644 --- a/packages/Amqp/src/Publisher/AmqpMessagePublisherConfiguration.php +++ b/packages/Amqp/src/Publisher/AmqpMessagePublisherConfiguration.php @@ -53,9 +53,11 @@ class AmqpMessagePublisherConfiguration */ private $defaultPersistentDelivery = true; - private bool $asyncPublishing = false; + private bool $batchPublishing = false; - private ?int $asyncPublishingTimeout = null; + private bool $nonBlockingConfirmation = false; + + private ?int $confirmationTimeout = null; private function __construct(string $connectionReference, string $exchangeName, ?string $outputDefaultConversionMediaType, string $referenceName) { @@ -155,25 +157,36 @@ public function getDefaultPersistentDelivery(): bool return $this->defaultPersistentDelivery; } - public function withAsyncPublishing(bool $enabled = true, ?int $timeoutInMilliseconds = null): AmqpMessagePublisherConfiguration + /** + * @param bool $batchPublishing coalesces published Messages into a single publisher confirms round trip + * @param bool $nonBlockingConfirmation publishes without waiting for publisher confirms, which are awaited on Future::resolve() or before the surrounding Command Bus or asynchronous endpoint finishes + * @param int|null $confirmationTimeoutInMilliseconds how long to await publisher confirms before treating the delivery as failed + */ + public function withHighThroughputPublishing(bool $batchPublishing = true, bool $nonBlockingConfirmation = true, ?int $confirmationTimeoutInMilliseconds = null): AmqpMessagePublisherConfiguration { - Assert::isTrue($timeoutInMilliseconds === null || $timeoutInMilliseconds > 0, 'Async publishing timeout must be a positive amount of milliseconds.'); - $this->asyncPublishing = $enabled; - if ($timeoutInMilliseconds !== null) { - $this->asyncPublishingTimeout = $timeoutInMilliseconds; + Assert::isTrue($confirmationTimeoutInMilliseconds === null || $confirmationTimeoutInMilliseconds > 0, 'Confirmation timeout must be a positive amount of milliseconds.'); + $this->batchPublishing = $batchPublishing; + $this->nonBlockingConfirmation = $nonBlockingConfirmation; + if ($confirmationTimeoutInMilliseconds !== null) { + $this->confirmationTimeout = $confirmationTimeoutInMilliseconds; } return $this; } - public function isAsyncPublishingEnabled(): bool + public function isBatchPublishingEnabled(): bool + { + return $this->batchPublishing; + } + + public function isNonBlockingConfirmationEnabled(): bool { - return $this->asyncPublishing; + return $this->nonBlockingConfirmation; } - public function getAsyncPublishingTimeout(): ?int + public function getConfirmationTimeout(): ?int { - return $this->asyncPublishingTimeout; + return $this->confirmationTimeout; } /** diff --git a/packages/Amqp/src/Publisher/AmqpMessagePublisherModule.php b/packages/Amqp/src/Publisher/AmqpMessagePublisherModule.php index b08a4bd6d..7206126b5 100644 --- a/packages/Amqp/src/Publisher/AmqpMessagePublisherModule.php +++ b/packages/Amqp/src/Publisher/AmqpMessagePublisherModule.php @@ -5,7 +5,7 @@ use Ecotone\Amqp\AmqpOutboundChannelAdapterBuilder; use Ecotone\AnnotationFinder\AnnotationFinder; use Ecotone\Messaging\Attribute\ModuleAnnotation; -use Ecotone\Messaging\Channel\AsyncPublishing\Config\AsyncPublishGatewayRegistration; +use Ecotone\Messaging\Channel\DeliveryConfirmation\Config\DeferredPublishingGatewayRegistration; use Ecotone\Messaging\Config\Annotation\AnnotationModule; use Ecotone\Messaging\Config\Annotation\ModuleConfiguration\ExtensionObjectResolver; use Ecotone\Messaging\Config\Configuration; @@ -89,11 +89,11 @@ public function prepare(Configuration $messagingConfiguration, array $extensionO ->withDefaultRoutingKey($amqpPublisher->getDefaultRoutingKey()) ->withRoutingKeyFromHeader($amqpPublisher->getRoutingKeyFromHeader()) ->withDefaultConversionMediaType($mediaType) - ->withAsyncPublishing($amqpPublisher->isAsyncPublishingEnabled(), $amqpPublisher->getAsyncPublishingTimeout()) - ->withAsyncPublishingChannelName($amqpPublisher->getReferenceName()) + ->withHighThroughputPublishing($amqpPublisher->isBatchPublishingEnabled(), $amqpPublisher->isNonBlockingConfirmationEnabled(), $amqpPublisher->getConfirmationTimeout()) + ->withPublishingChannelName($amqpPublisher->getReferenceName()) ); - AsyncPublishGatewayRegistration::registerFor($messagingConfiguration, $amqpPublisher->getReferenceName(), $amqpPublisher->isAsyncPublishingEnabled()); + DeferredPublishingGatewayRegistration::registerFor($messagingConfiguration, $amqpPublisher->getReferenceName(), $amqpPublisher->isNonBlockingConfirmationEnabled()); } } diff --git a/packages/Amqp/tests/Fixture/AsyncPublishing/OrderWasPlaced.php b/packages/Amqp/tests/Fixture/HighThroughputPublishing/OrderWasPlaced.php similarity index 72% rename from packages/Amqp/tests/Fixture/AsyncPublishing/OrderWasPlaced.php rename to packages/Amqp/tests/Fixture/HighThroughputPublishing/OrderWasPlaced.php index 1787fab3b..18d46c8fc 100644 --- a/packages/Amqp/tests/Fixture/AsyncPublishing/OrderWasPlaced.php +++ b/packages/Amqp/tests/Fixture/HighThroughputPublishing/OrderWasPlaced.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Test\Ecotone\Amqp\Fixture\AsyncPublishing; +namespace Test\Ecotone\Amqp\Fixture\HighThroughputPublishing; /** * licence Apache-2.0 diff --git a/packages/Amqp/tests/Integration/AsyncPublishingReliabilityTest.php b/packages/Amqp/tests/Integration/HighThroughputPublishingReliabilityTest.php similarity index 85% rename from packages/Amqp/tests/Integration/AsyncPublishingReliabilityTest.php rename to packages/Amqp/tests/Integration/HighThroughputPublishingReliabilityTest.php index c566efbfa..8c0b921e4 100644 --- a/packages/Amqp/tests/Integration/AsyncPublishingReliabilityTest.php +++ b/packages/Amqp/tests/Integration/HighThroughputPublishingReliabilityTest.php @@ -8,7 +8,7 @@ use Ecotone\Amqp\Publisher\AmqpMessagePublisherConfiguration; use Ecotone\Lite\EcotoneLite; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Config\ModulePackageList; use Ecotone\Messaging\Config\ServiceConfiguration; use Ecotone\Messaging\MessagePublisher; @@ -23,7 +23,7 @@ * licence Apache-2.0 * @internal */ -final class AsyncPublishingReliabilityTest extends AmqpMessagingTestCase +final class HighThroughputPublishingReliabilityTest extends AmqpMessagingTestCase { public function test_nacked_message_fails_delivery_confirmation_over_amqp_lib(): void { @@ -33,7 +33,7 @@ public function test_nacked_message_fails_delivery_confirmation_over_amqp_lib(): $this->expectException(PublishingFailedException::class); - $publisher->asyncPublish( + $publisher->publishDeferred( BatchMessage::constructEmpty() ->append('first message fills the queue') ->append('second message overflows and gets nacked') @@ -48,7 +48,7 @@ public function test_nacked_message_fails_delivery_confirmation_over_amqp_ext(): $this->expectException(PublishingFailedException::class); - $publisher->asyncPublish( + $publisher->publishDeferred( BatchMessage::constructEmpty() ->append('first message fills the queue') ->append('second message overflows and gets nacked') @@ -92,7 +92,7 @@ public function test_unroutable_message_fails_delivery_confirmation_over_amqp_li $this->expectException(PublishingFailedException::class); - $publisher->asyncPublish('order that routes nowhere')->resolve(); + $publisher->publishDeferred('order that routes nowhere')->resolve(); } public function test_unroutable_message_fails_delivery_confirmation_over_amqp_ext(): void @@ -102,7 +102,7 @@ public function test_unroutable_message_fails_delivery_confirmation_over_amqp_ex $this->expectException(PublishingFailedException::class); - $publisher->asyncPublish('order that routes nowhere')->resolve(); + $publisher->publishDeferred('order that routes nowhere')->resolve(); } public function test_each_future_reports_outcome_of_its_own_message_when_sharing_channel(): void @@ -111,8 +111,8 @@ public function test_each_future_reports_outcome_of_its_own_message_when_sharing $queueName = $this->declareQueue($libConnectionFactory); $publisher = $this->bootstrapPublisherWithRoutingKeyFromHeader($libConnectionFactory); - $routableFuture = $publisher->asyncPublish('order that reaches the queue', metadata: ['routingKey' => $queueName]); - $unroutableFuture = $publisher->asyncPublish('order that routes nowhere', metadata: ['routingKey' => Uuid::v7()->toRfc4122()]); + $routableFuture = $publisher->publishDeferred('order that reaches the queue', metadata: ['routingKey' => $queueName]); + $unroutableFuture = $publisher->publishDeferred('order that routes nowhere', metadata: ['routingKey' => Uuid::v7()->toRfc4122()]); $routableFuture->resolve(); @@ -127,8 +127,8 @@ public function test_each_future_reports_outcome_of_its_own_message_when_sharing $queueName = $this->declareQueue($extConnectionFactory); $publisher = $this->bootstrapPublisherWithRoutingKeyFromHeader($extConnectionFactory); - $routableFuture = $publisher->asyncPublish('order that reaches the queue', metadata: ['routingKey' => $queueName]); - $unroutableFuture = $publisher->asyncPublish('order that routes nowhere', metadata: ['routingKey' => Uuid::v7()->toRfc4122()]); + $routableFuture = $publisher->publishDeferred('order that reaches the queue', metadata: ['routingKey' => $queueName]); + $unroutableFuture = $publisher->publishDeferred('order that routes nowhere', metadata: ['routingKey' => Uuid::v7()->toRfc4122()]); $routableFuture->resolve(); @@ -144,10 +144,10 @@ public function test_nack_arriving_during_other_future_await_fails_only_nacked_f $overflowQueue = $this->declareQueueRejectingOverflow($libConnectionFactory); $publisher = $this->bootstrapPublisherWithRoutingKeyFromHeader($libConnectionFactory); - $publisher->asyncPublish('filler order', metadata: ['routingKey' => $overflowQueue])->resolve(); + $publisher->publishDeferred('filler order', metadata: ['routingKey' => $overflowQueue])->resolve(); - $deliveredFuture = $publisher->asyncPublish('delivered order', metadata: ['routingKey' => $normalQueue]); - $nackedFuture = $publisher->asyncPublish('nacked order', metadata: ['routingKey' => $overflowQueue]); + $deliveredFuture = $publisher->publishDeferred('delivered order', metadata: ['routingKey' => $normalQueue]); + $nackedFuture = $publisher->publishDeferred('nacked order', metadata: ['routingKey' => $overflowQueue]); $deliveredFuture->resolve(); @@ -163,10 +163,10 @@ public function test_nack_arriving_during_other_future_await_fails_only_nacked_f $overflowQueue = $this->declareQueueRejectingOverflow($extConnectionFactory); $publisher = $this->bootstrapPublisherWithRoutingKeyFromHeader($extConnectionFactory); - $publisher->asyncPublish('filler order', metadata: ['routingKey' => $overflowQueue])->resolve(); + $publisher->publishDeferred('filler order', metadata: ['routingKey' => $overflowQueue])->resolve(); - $deliveredFuture = $publisher->asyncPublish('delivered order', metadata: ['routingKey' => $normalQueue]); - $nackedFuture = $publisher->asyncPublish('nacked order', metadata: ['routingKey' => $overflowQueue]); + $deliveredFuture = $publisher->publishDeferred('delivered order', metadata: ['routingKey' => $normalQueue]); + $nackedFuture = $publisher->publishDeferred('nacked order', metadata: ['routingKey' => $overflowQueue]); $deliveredFuture->resolve(); @@ -181,7 +181,7 @@ public function test_only_failing_message_from_batch_is_reported_with_per_messag $queueName = $this->declareQueue($libConnectionFactory); $publisher = $this->bootstrapPublisherWithRoutingKeyFromHeader($libConnectionFactory); - $future = $publisher->asyncPublish( + $future = $publisher->publishDeferred( BatchMessage::constructEmpty() ->append('first delivered order', ['routingKey' => $queueName]) ->append('order that routes nowhere', ['routingKey' => Uuid::v7()->toRfc4122()]) @@ -240,7 +240,7 @@ private function bootstrapPublisherWithRoutingKeyFromHeader(AmqpLibConnection|Am AmqpMessagePublisherConfiguration::create() ->withAutoDeclareQueueOnSend(false) ->withRoutingKeyFromHeader('routingKey') - ->withAsyncPublishing(timeoutInMilliseconds: 3000), + ->withHighThroughputPublishing(confirmationTimeoutInMilliseconds: 3000), ]), licenceKey: LicenceTesting::VALID_LICENCE, ); @@ -273,7 +273,7 @@ private function bootstrapPublisher(AmqpLibConnection|AmqpConnectionFactory $con AmqpMessagePublisherConfiguration::create() ->withAutoDeclareQueueOnSend(false) ->withDefaultRoutingKey($queueName) - ->withAsyncPublishing(timeoutInMilliseconds: 3000), + ->withHighThroughputPublishing(confirmationTimeoutInMilliseconds: 3000), ]), licenceKey: LicenceTesting::VALID_LICENCE, ); diff --git a/packages/Amqp/tests/Integration/AsyncPublishingTest.php b/packages/Amqp/tests/Integration/HighThroughputPublishingTest.php similarity index 71% rename from packages/Amqp/tests/Integration/AsyncPublishingTest.php rename to packages/Amqp/tests/Integration/HighThroughputPublishingTest.php index 7b131ccb9..a45e53bf2 100644 --- a/packages/Amqp/tests/Integration/AsyncPublishingTest.php +++ b/packages/Amqp/tests/Integration/HighThroughputPublishingTest.php @@ -11,7 +11,8 @@ use Ecotone\Messaging\Attribute\Asynchronous; use Ecotone\Messaging\Attribute\Parameter\Reference; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; +use Ecotone\Messaging\Config\ConfigurationException; use Ecotone\Messaging\Config\ModulePackageList; use Ecotone\Messaging\Config\ServiceConfiguration; use Ecotone\Messaging\Endpoint\ExecutionPollingMetadata; @@ -30,15 +31,15 @@ use Enqueue\AmqpLib\AmqpConnectionFactory as AmqpLibConnection; use Symfony\Component\Uid\Uuid; use Test\Ecotone\Amqp\AmqpMessagingTestCase; -use Test\Ecotone\Amqp\Fixture\AsyncPublishing\OrderWasPlaced; +use Test\Ecotone\Amqp\Fixture\HighThroughputPublishing\OrderWasPlaced; /** * licence Apache-2.0 * @internal */ -final class AsyncPublishingTest extends AmqpMessagingTestCase +final class HighThroughputPublishingTest extends AmqpMessagingTestCase { - public function test_multiple_messages_published_asynchronously_from_command_handler_are_delivered(): void + public function test_multiple_messages_published_from_command_handler_are_delivered(): void { $channelName = Uuid::v7()->toRfc4122(); $orderService = $this->createOrderService($channelName); @@ -53,7 +54,7 @@ public function test_multiple_messages_published_asynchronously_from_command_han $this->assertCount(3, $messaging->sendQueryWithRouting('order.getReceived')); } - public function test_async_publishing_requires_enterprise_licence(): void + public function test_high_throughput_publishing_requires_enterprise_licence(): void { $channelName = Uuid::v7()->toRfc4122(); $orderService = $this->createOrderService($channelName); @@ -63,7 +64,7 @@ public function test_async_publishing_requires_enterprise_licence(): void $this->bootstrapEcotone($channelName, $orderService, licenceKey: null); } - public function test_async_publishing_via_message_publisher_requires_enterprise_licence(): void + public function test_high_throughput_publishing_via_message_publisher_requires_enterprise_licence(): void { $this->expectException(LicensingException::class); @@ -75,12 +76,12 @@ public function test_async_publishing_via_message_publisher_requires_enterprise_ ->withExtensionObjects([ AmqpMessagePublisherConfiguration::create() ->withDefaultRoutingKey(Uuid::v7()->toRfc4122()) - ->withAsyncPublishing(), + ->withHighThroughputPublishing(), ]), ); } - public function test_async_publish_on_publisher_without_async_configuration_throws_before_publishing(): void + public function test_publish_deferred_on_publisher_without_non_blocking_confirmation_throws_before_publishing(): void { $queueName = Uuid::v7()->toRfc4122(); $messaging = EcotoneLite::bootstrapFlowTesting( @@ -99,7 +100,7 @@ public function test_async_publish_on_publisher_without_async_configuration_thro $publishFailed = false; try { - $publisher->asyncPublish('order that must not be published'); + $publisher->publishDeferred('order that must not be published'); } catch (PublishingFailedException) { $publishFailed = true; } @@ -108,7 +109,7 @@ public function test_async_publish_on_publisher_without_async_configuration_thro $this->assertNull($messaging->getMessageChannel('verificationChannel')->receiveWithTimeout(PollingMetadata::create('verification')->setFixedRateInMilliseconds(200))); } - public function test_message_publisher_async_publish_confirms_delivery_on_future_resolve(): void + public function test_publish_deferred_confirms_delivery_on_future_resolve(): void { $queueName = Uuid::v7()->toRfc4122(); $context = self::getRabbitConnectionFactory()->createContext(); @@ -122,14 +123,14 @@ public function test_message_publisher_async_publish_confirms_delivery_on_future AmqpMessagePublisherConfiguration::create() ->withAutoDeclareQueueOnSend(true) ->withDefaultRoutingKey($queueName) - ->withAsyncPublishing(), + ->withHighThroughputPublishing(), ]), licenceKey: LicenceTesting::VALID_LICENCE, ); $publisher = $messaging->getGateway(MessagePublisher::class); - $singleFuture = $publisher->asyncPublish('single order'); - $batchFuture = $publisher->asyncPublish( + $singleFuture = $publisher->publishDeferred('single order'); + $batchFuture = $publisher->publishDeferred( BatchMessage::constructEmpty() ->append('first order') ->append('second order', ['priority' => '5']) @@ -172,7 +173,7 @@ public function test_delayed_entry_of_published_batch_is_delivered_after_delay() $messaging = $this->bootstrapPublisherWithVerificationChannel($queueName); $publisher = $messaging->getGateway(MessagePublisher::class); - $publisher->asyncPublish( + $publisher->publishDeferred( BatchMessage::constructEmpty() ->append('immediate order') ->append('delayed order', [MessageHeaders::DELIVERY_DELAY => 2000]) @@ -191,7 +192,7 @@ public function test_expired_entry_of_published_batch_is_not_delivered(): void $messaging = $this->bootstrapPublisherWithVerificationChannel($queueName); $publisher = $messaging->getGateway(MessagePublisher::class); - $publisher->asyncPublish( + $publisher->publishDeferred( BatchMessage::constructEmpty() ->append('expiring order', [MessageHeaders::TIME_TO_LIVE => 100]) ->append('kept order') @@ -235,6 +236,74 @@ public function test_batch_published_over_amqp_lib_connection_is_delivered(): vo ); } + public function test_batch_publishing_without_non_blocking_confirmation_delivers_batch_and_confirms_before_returning(): void + { + $queueName = Uuid::v7()->toRfc4122(); + $messaging = $this->bootstrapPublisherWithVerificationChannel( + $queueName, + publisherConfigurator: fn (AmqpMessagePublisherConfiguration $configuration): AmqpMessagePublisherConfiguration => $configuration->withHighThroughputPublishing(nonBlockingConfirmation: false), + ); + $publisher = $messaging->getGateway(MessagePublisher::class); + + $publisher->convertAndSend( + BatchMessage::constructEmpty() + ->append('first order') + ->append('second order') + ); + + $verificationChannel = $messaging->getMessageChannel('verificationChannel'); + $receivedPayloads = [ + $verificationChannel->receive()->getPayload(), + $verificationChannel->receive()->getPayload(), + ]; + sort($receivedPayloads); + $this->assertSame(['first order', 'second order'], $receivedPayloads); + } + + public function test_batch_publishing_without_non_blocking_confirmation_does_not_offer_publish_deferred(): void + { + $queueName = Uuid::v7()->toRfc4122(); + $messaging = $this->bootstrapPublisherWithVerificationChannel( + $queueName, + publisherConfigurator: fn (AmqpMessagePublisherConfiguration $configuration): AmqpMessagePublisherConfiguration => $configuration->withHighThroughputPublishing(nonBlockingConfirmation: false), + ); + + $this->expectException(PublishingFailedException::class); + $this->expectExceptionMessageMatches('/not configured for non blocking confirmation/'); + + $messaging->getGateway(MessagePublisher::class)->publishDeferred('order that must not be published'); + } + + public function test_non_blocking_confirmation_without_batch_publishing_rejects_batch_message(): void + { + $queueName = Uuid::v7()->toRfc4122(); + $messaging = $this->bootstrapPublisherWithVerificationChannel( + $queueName, + publisherConfigurator: fn (AmqpMessagePublisherConfiguration $configuration): AmqpMessagePublisherConfiguration => $configuration->withHighThroughputPublishing(batchPublishing: false), + ); + + $this->expectException(ConfigurationException::class); + $this->expectExceptionMessageMatches('/requires batch publishing to be enabled/'); + + $messaging->getGateway(MessagePublisher::class)->convertAndSend( + BatchMessage::constructEmpty()->append('first order') + ); + } + + public function test_non_blocking_confirmation_without_batch_publishing_still_defers_single_message_confirmation(): void + { + $queueName = Uuid::v7()->toRfc4122(); + $messaging = $this->bootstrapPublisherWithVerificationChannel( + $queueName, + publisherConfigurator: fn (AmqpMessagePublisherConfiguration $configuration): AmqpMessagePublisherConfiguration => $configuration->withHighThroughputPublishing(batchPublishing: false), + ); + + $future = $messaging->getGateway(MessagePublisher::class)->publishDeferred('single order'); + + $this->assertNull($future->resolve()); + $this->assertSame('single order', $messaging->getMessageChannel('verificationChannel')->receive()->getPayload()); + } + private function createOrderService(string $channelName): object { return new class ($channelName) { @@ -268,8 +337,14 @@ public function getReceived(): array }; } - private function bootstrapPublisherWithVerificationChannel(string $queueName, ?object $commandHandler = null): FlowTestSupport + private function bootstrapPublisherWithVerificationChannel(string $queueName, ?object $commandHandler = null, ?callable $publisherConfigurator = null): FlowTestSupport { + $publisherConfiguration = AmqpMessagePublisherConfiguration::create() + ->withDefaultRoutingKey($queueName); + $publisherConfiguration = $publisherConfigurator === null + ? $publisherConfiguration->withHighThroughputPublishing() + : $publisherConfigurator($publisherConfiguration); + return EcotoneLite::bootstrapFlowTesting( $commandHandler === null ? [] : [$commandHandler::class], $commandHandler === null @@ -278,9 +353,7 @@ private function bootstrapPublisherWithVerificationChannel(string $queueName, ?o ServiceConfiguration::createWithDefaults() ->withSkippedModulePackageNames(ModulePackageList::allPackagesExcept([ModulePackageList::ASYNCHRONOUS_PACKAGE, ModulePackageList::AMQP_PACKAGE])) ->withExtensionObjects([ - AmqpMessagePublisherConfiguration::create() - ->withDefaultRoutingKey($queueName) - ->withAsyncPublishing(), + $publisherConfiguration, AmqpBackedMessageChannelBuilder::create('verificationChannel', queueName: $queueName), ]), licenceKey: LicenceTesting::VALID_LICENCE, diff --git a/packages/Dbal/src/Configuration/DbalMessagePublisherConfiguration.php b/packages/Dbal/src/Configuration/DbalMessagePublisherConfiguration.php index 16711c084..72a10b67f 100644 --- a/packages/Dbal/src/Configuration/DbalMessagePublisherConfiguration.php +++ b/packages/Dbal/src/Configuration/DbalMessagePublisherConfiguration.php @@ -35,7 +35,7 @@ class DbalMessagePublisherConfiguration */ private $queueName; - private bool $asyncPublishing = false; + private bool $batchPublishing = false; private function __construct(string $connectionReference, string $queueName, ?string $outputDefaultConversionMediaType, string $referenceName) { @@ -122,15 +122,19 @@ public function getReferenceName(): string return $this->referenceName; } - public function withAsyncPublishing(bool $asyncPublishing = true): self + /** + * Coalesces published Messages into a single multi row insert. + * Non blocking confirmation is not offered here, as the insert blocks until the database confirms it. + */ + public function withHighThroughputPublishing(): self { - $this->asyncPublishing = $asyncPublishing; + $this->batchPublishing = true; return $this; } - public function isAsyncPublishingEnabled(): bool + public function isBatchPublishingEnabled(): bool { - return $this->asyncPublishing; + return $this->batchPublishing; } } diff --git a/packages/Dbal/src/Configuration/DbalPublisherModule.php b/packages/Dbal/src/Configuration/DbalPublisherModule.php index 0446c6123..54650359e 100644 --- a/packages/Dbal/src/Configuration/DbalPublisherModule.php +++ b/packages/Dbal/src/Configuration/DbalPublisherModule.php @@ -8,7 +8,7 @@ use Ecotone\Dbal\DbalBackedMessageChannelBuilder; use Ecotone\Dbal\DbalOutboundChannelAdapterBuilder; use Ecotone\Messaging\Attribute\ModuleAnnotation; -use Ecotone\Messaging\Channel\AsyncPublishing\Config\AsyncPublishGatewayRegistration; +use Ecotone\Messaging\Channel\DeliveryConfirmation\Config\DeferredPublishingGatewayRegistration; use Ecotone\Messaging\Config\Annotation\AnnotationModule; use Ecotone\Messaging\Config\Annotation\ModuleConfiguration\ExtensionObjectResolver; use Ecotone\Messaging\Config\Configuration; @@ -117,10 +117,10 @@ public function prepare(Configuration $messagingConfiguration, array $extensionO ->withAutoDeclareOnSend($dbalPublisher->isAutoDeclareQueueOnSend()) ->withHeaderMapper($dbalPublisher->getHeaderMapper()) ->withDefaultConversionMediaType($mediaType) - ->withAsyncPublishing($dbalPublisher->isAsyncPublishingEnabled()) + ->withBatchPublishing($dbalPublisher->isBatchPublishingEnabled()) ); - AsyncPublishGatewayRegistration::registerFor($messagingConfiguration, $dbalPublisher->getReferenceName(), $dbalPublisher->isAsyncPublishingEnabled()); + DeferredPublishingGatewayRegistration::registerFor($messagingConfiguration, $dbalPublisher->getReferenceName(), nonBlockingConfirmationEnabled: false); } } diff --git a/packages/Dbal/src/DbalBackedMessageChannelBuilder.php b/packages/Dbal/src/DbalBackedMessageChannelBuilder.php index e08f96825..639e49d20 100644 --- a/packages/Dbal/src/DbalBackedMessageChannelBuilder.php +++ b/packages/Dbal/src/DbalBackedMessageChannelBuilder.php @@ -31,16 +31,20 @@ public static function create(string $channelName, string $connectionReferenceNa return new self($channelName, $connectionReferenceName); } - public function withHighThroughputPublishing(bool $enabled = true): self + /** + * Coalesces published Messages into a single multi row insert. + * Non blocking confirmation is not offered here, as the insert blocks until the database confirms it. + */ + public function withHighThroughputPublishing(): self { - $this->getDbalOutboundChannelAdapter()->withAsyncPublishing($enabled); + $this->getDbalOutboundChannelAdapter()->withBatchPublishing(); return $this; } protected function supportsBatchMessages(): bool { - return $this->getDbalOutboundChannelAdapter()->isAsyncPublishingEnabled(); + return $this->getDbalOutboundChannelAdapter()->isBatchPublishingEnabled(); } private function getDbalOutboundChannelAdapter(): DbalOutboundChannelAdapterBuilder diff --git a/packages/Dbal/src/DbalOutboundChannelAdapter.php b/packages/Dbal/src/DbalOutboundChannelAdapter.php index 7dc72077a..78baf93a4 100644 --- a/packages/Dbal/src/DbalOutboundChannelAdapter.php +++ b/packages/Dbal/src/DbalOutboundChannelAdapter.php @@ -8,7 +8,6 @@ use Ecotone\Enqueue\CachedConnectionFactory; use Ecotone\Enqueue\EnqueueOutboundChannelAdapter; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; use Ecotone\Messaging\Channel\PollableChannel\Serialization\OutboundMessageConverter; use Ecotone\Messaging\Conversion\ConversionService; use Ecotone\Messaging\Message; @@ -30,8 +29,7 @@ public function __construct( OutboundMessageConverter $outboundMessageConverter, ConversionService $conversionService, private EnqueueTableManager $tableManager, - AsyncPublishingRegistry $asyncPublishingRegistry, - bool $asyncPublishing = false, + bool $batchPublishing = false, ) { parent::__construct( $connectionFactory, @@ -39,8 +37,7 @@ public function __construct( $autoDeclare, $outboundMessageConverter, $conversionService, - $asyncPublishingRegistry, - $asyncPublishing, + $batchPublishing, $this->queueName, ); } diff --git a/packages/Dbal/src/DbalOutboundChannelAdapterBuilder.php b/packages/Dbal/src/DbalOutboundChannelAdapterBuilder.php index 7d01a237b..2bc5d363e 100644 --- a/packages/Dbal/src/DbalOutboundChannelAdapterBuilder.php +++ b/packages/Dbal/src/DbalOutboundChannelAdapterBuilder.php @@ -5,7 +5,6 @@ use Ecotone\Dbal\Database\EnqueueTableManager; use Ecotone\Enqueue\CachedConnectionFactory; use Ecotone\Enqueue\EnqueueOutboundChannelAdapterBuilder; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; use Ecotone\Messaging\Channel\PollableChannel\Serialization\OutboundMessageConverter; use Ecotone\Messaging\Config\Container\Definition; use Ecotone\Messaging\Config\Container\MessagingContainerBuilder; @@ -28,7 +27,7 @@ class DbalOutboundChannelAdapterBuilder extends EnqueueOutboundChannelAdapterBui */ private $connectionFactoryReferenceName; - private bool $asyncPublishing = false; + private bool $batchPublishing = false; private function __construct(string $queueName, string $connectionFactoryReferenceName) { @@ -42,22 +41,22 @@ public static function create(string $queueName, string $connectionFactoryRefere return new self($queueName, $connectionFactoryReferenceName); } - public function withAsyncPublishing(bool $asyncPublishing = true): self + public function withBatchPublishing(bool $batchPublishing = true): self { - $this->asyncPublishing = $asyncPublishing; + $this->batchPublishing = $batchPublishing; return $this; } - public function isAsyncPublishingEnabled(): bool + public function isBatchPublishingEnabled(): bool { - return $this->asyncPublishing; + return $this->batchPublishing; } public function compile(MessagingContainerBuilder $builder): Definition { - if ($this->asyncPublishing && ! $builder->getServiceConfiguration()->isRunningForEnterprise()) { - throw LicensingException::create('Asynchronous publishing is available only with Ecotone Enterprise licence.'); + if ($this->batchPublishing && ! $builder->getServiceConfiguration()->isRunningForEnterprise()) { + throw LicensingException::create('High Throughput Publishing is available only with Ecotone Enterprise licence.'); } $connectionFactory = new Definition(CachedConnectionFactory::class, [ @@ -82,8 +81,7 @@ public function compile(MessagingContainerBuilder $builder): Definition $outboundMessageConverter, new Reference(ConversionService::REFERENCE_NAME), new Reference(EnqueueTableManager::class), - new Reference(AsyncPublishingRegistry::class), - $this->asyncPublishing, + $this->batchPublishing, ]); } } diff --git a/packages/Dbal/tests/Fixture/AsyncPublishing/AsyncPublishingTestChannel.php b/packages/Dbal/tests/Fixture/HighThroughputPublishing/HighThroughputTestChannel.php similarity index 77% rename from packages/Dbal/tests/Fixture/AsyncPublishing/AsyncPublishingTestChannel.php rename to packages/Dbal/tests/Fixture/HighThroughputPublishing/HighThroughputTestChannel.php index ccfad310a..fdc8c3e84 100644 --- a/packages/Dbal/tests/Fixture/AsyncPublishing/AsyncPublishingTestChannel.php +++ b/packages/Dbal/tests/Fixture/HighThroughputPublishing/HighThroughputTestChannel.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace Test\Ecotone\Dbal\Fixture\AsyncPublishing; +namespace Test\Ecotone\Dbal\Fixture\HighThroughputPublishing; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; use Ecotone\Messaging\Channel\BatchSupportingMessageChannel; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDeliveryRegistry; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Endpoint\PollingMetadata; use Ecotone\Messaging\Message; use Ecotone\Messaging\PollableChannel; @@ -16,14 +16,14 @@ /** * licence Apache-2.0 */ -final class AsyncPublishingTestChannel implements PollableChannel, BatchSupportingMessageChannel +final class HighThroughputTestChannel implements PollableChannel, BatchSupportingMessageChannel { /** @var Message[] */ private array $queue = []; public function __construct( private string $channelName, - private AsyncPublishingRegistry $asyncPublishingRegistry, + private PendingDeliveryRegistry $pendingDeliveryRegistry, private ?string $deliveryFailureReason = null, ) { } @@ -44,7 +44,7 @@ public function send(Message $message): void $pendingDelivery = new TestPendingDelivery($message, $this->channelName, $this->deliveryFailureReason); - if (! $this->asyncPublishingRegistry->isScopeActive()) { + if (! $this->pendingDeliveryRegistry->isScopeActive()) { $deliveryResult = $pendingDelivery->awaitDelivery(); if (! $deliveryResult->isSuccessful()) { throw PublishingFailedException::withFailedDeliveries($deliveryResult->getFailedDeliveries()); @@ -53,7 +53,7 @@ public function send(Message $message): void return; } - $this->asyncPublishingRegistry->register($this->channelName, $pendingDelivery); + $this->pendingDeliveryRegistry->register($this->channelName, $pendingDelivery); } public function receive(): ?Message diff --git a/packages/Dbal/tests/Fixture/AsyncPublishing/AsyncPublishingTestChannelBuilder.php b/packages/Dbal/tests/Fixture/HighThroughputPublishing/HighThroughputTestChannelBuilder.php similarity index 74% rename from packages/Dbal/tests/Fixture/AsyncPublishing/AsyncPublishingTestChannelBuilder.php rename to packages/Dbal/tests/Fixture/HighThroughputPublishing/HighThroughputTestChannelBuilder.php index 56d0d2877..a41cda829 100644 --- a/packages/Dbal/tests/Fixture/AsyncPublishing/AsyncPublishingTestChannelBuilder.php +++ b/packages/Dbal/tests/Fixture/HighThroughputPublishing/HighThroughputTestChannelBuilder.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace Test\Ecotone\Dbal\Fixture\AsyncPublishing; +namespace Test\Ecotone\Dbal\Fixture\HighThroughputPublishing; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDeliveryRegistry; use Ecotone\Messaging\Channel\MessageChannelBuilder; use Ecotone\Messaging\Config\Container\Definition; use Ecotone\Messaging\Config\Container\MessagingContainerBuilder; @@ -13,7 +13,7 @@ /** * licence Apache-2.0 */ -final class AsyncPublishingTestChannelBuilder implements MessageChannelBuilder +final class HighThroughputTestChannelBuilder implements MessageChannelBuilder { private function __construct(private string $channelName, private ?string $deliveryFailureReason) { @@ -41,9 +41,9 @@ public function isStreamingChannel(): bool public function compile(MessagingContainerBuilder $builder): Definition|Reference { - return new Definition(AsyncPublishingTestChannel::class, [ + return new Definition(HighThroughputTestChannel::class, [ $this->channelName, - new Reference(AsyncPublishingRegistry::class), + new Reference(PendingDeliveryRegistry::class), $this->deliveryFailureReason, ]); } diff --git a/packages/Redis/tests/Fixture/AsyncPublishing/OrderWasPlaced.php b/packages/Dbal/tests/Fixture/HighThroughputPublishing/OrderWasPlaced.php similarity index 72% rename from packages/Redis/tests/Fixture/AsyncPublishing/OrderWasPlaced.php rename to packages/Dbal/tests/Fixture/HighThroughputPublishing/OrderWasPlaced.php index 1bb32fa51..391d29c7e 100644 --- a/packages/Redis/tests/Fixture/AsyncPublishing/OrderWasPlaced.php +++ b/packages/Dbal/tests/Fixture/HighThroughputPublishing/OrderWasPlaced.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Test\Ecotone\Redis\Fixture\AsyncPublishing; +namespace Test\Ecotone\Dbal\Fixture\HighThroughputPublishing; /** * licence Apache-2.0 diff --git a/packages/Dbal/tests/Fixture/AsyncPublishing/TestPendingDelivery.php b/packages/Dbal/tests/Fixture/HighThroughputPublishing/TestPendingDelivery.php similarity index 75% rename from packages/Dbal/tests/Fixture/AsyncPublishing/TestPendingDelivery.php rename to packages/Dbal/tests/Fixture/HighThroughputPublishing/TestPendingDelivery.php index 388a3102e..2c391b16b 100644 --- a/packages/Dbal/tests/Fixture/AsyncPublishing/TestPendingDelivery.php +++ b/packages/Dbal/tests/Fixture/HighThroughputPublishing/TestPendingDelivery.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace Test\Ecotone\Dbal\Fixture\AsyncPublishing; +namespace Test\Ecotone\Dbal\Fixture\HighThroughputPublishing; -use Ecotone\Messaging\Channel\AsyncPublishing\DeliveryResult; -use Ecotone\Messaging\Channel\AsyncPublishing\FailedDelivery; -use Ecotone\Messaging\Channel\AsyncPublishing\PendingDelivery; +use Ecotone\Messaging\Channel\DeliveryConfirmation\DeliveryResult; +use Ecotone\Messaging\Channel\DeliveryConfirmation\FailedDelivery; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDelivery; use Ecotone\Messaging\Message; /** diff --git a/packages/Dbal/tests/Integration/AsyncPublishingTest.php b/packages/Dbal/tests/Integration/HighThroughputPublishingTest.php similarity index 81% rename from packages/Dbal/tests/Integration/AsyncPublishingTest.php rename to packages/Dbal/tests/Integration/HighThroughputPublishingTest.php index bdb4b0fbf..f97e60d6d 100644 --- a/packages/Dbal/tests/Integration/AsyncPublishingTest.php +++ b/packages/Dbal/tests/Integration/HighThroughputPublishingTest.php @@ -11,7 +11,7 @@ use Ecotone\Messaging\Attribute\Asynchronous; use Ecotone\Messaging\Attribute\Parameter\Reference; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Config\ConfigurationException; use Ecotone\Messaging\Config\ModulePackageList; use Ecotone\Messaging\Config\ServiceConfiguration; @@ -32,15 +32,15 @@ use Interop\Queue\Exception\Exception; use Symfony\Component\Uid\Uuid; use Test\Ecotone\Dbal\DbalMessagingTestCase; -use Test\Ecotone\Dbal\Fixture\AsyncPublishing\OrderWasPlaced; +use Test\Ecotone\Dbal\Fixture\HighThroughputPublishing\OrderWasPlaced; /** * licence Apache-2.0 * @internal */ -final class AsyncPublishingTest extends DbalMessagingTestCase +final class HighThroughputPublishingTest extends DbalMessagingTestCase { - public function test_multiple_messages_published_asynchronously_from_command_handler_are_delivered(): void + public function test_multiple_messages_published_from_command_handler_are_delivered(): void { $orderService = $this->createOrderService(); $messaging = $this->bootstrapEcotoneWithChannel($orderService, LicenceTesting::VALID_LICENCE); @@ -57,7 +57,7 @@ public function test_multiple_messages_published_asynchronously_from_command_han ); } - public function test_async_publishing_requires_enterprise_licence(): void + public function test_high_throughput_publishing_requires_enterprise_licence(): void { $orderService = $this->createOrderService(); @@ -66,7 +66,7 @@ public function test_async_publishing_requires_enterprise_licence(): void $this->bootstrapEcotoneWithChannel($orderService, licenceKey: null); } - public function test_async_publishing_via_message_publisher_requires_enterprise_licence(): void + public function test_high_throughput_publishing_via_message_publisher_requires_enterprise_licence(): void { $this->expectException(LicensingException::class); @@ -77,44 +77,36 @@ public function test_async_publishing_via_message_publisher_requires_enterprise_ ->withSkippedModulePackageNames(ModulePackageList::allPackagesExcept([ModulePackageList::DBAL_PACKAGE])) ->withExtensionObjects([ DbalMessagePublisherConfiguration::create(MessagePublisher::class, Uuid::v7()->toRfc4122()) - ->withAsyncPublishing(), + ->withHighThroughputPublishing(), ]), ); } - public function test_async_publish_on_publisher_without_async_configuration_throws_before_publishing(): void + public function test_publish_deferred_is_not_available_as_confirmation_can_not_be_deferred(): void { $queueName = Uuid::v7()->toRfc4122(); - $messaging = $this->bootstrapPublisher($queueName, asyncPublishing: false); + $messaging = $this->bootstrapPublisher($queueName, highThroughputPublishing: true); $publisher = $messaging->getGateway(MessagePublisher::class); - $publishFailed = false; - try { - $publisher->asyncPublish('order that must not be published'); - } catch (PublishingFailedException) { - $publishFailed = true; - } + $this->expectException(PublishingFailedException::class); + $this->expectExceptionMessageMatches('/not configured for non blocking confirmation/'); - $this->assertTrue($publishFailed); - $this->assertNull($messaging->getMessageChannel($queueName)->receive()); + $publisher->publishDeferred('order that must not be published'); } - public function test_message_publisher_async_publish_confirms_delivery_on_future_resolve(): void + public function test_message_publisher_delivers_single_and_batched_messages(): void { $queueName = Uuid::v7()->toRfc4122(); - $messaging = $this->bootstrapPublisher($queueName, asyncPublishing: true); + $messaging = $this->bootstrapPublisher($queueName, highThroughputPublishing: true); $publisher = $messaging->getGateway(MessagePublisher::class); - $singleFuture = $publisher->asyncPublish('single order'); - $batchFuture = $publisher->asyncPublish( + $publisher->send('single order'); + $publisher->convertAndSend( BatchMessage::constructEmpty() ->append('first order') ->append('second order', ['priority' => '5']) ); - $this->assertNull($singleFuture->resolve()); - $this->assertNull($batchFuture->resolve()); - $receivedPayloads = []; while ($message = $messaging->getMessageChannel($queueName)->receive()) { $receivedPayloads[] = $message->getPayload(); @@ -123,10 +115,10 @@ public function test_message_publisher_async_publish_confirms_delivery_on_future $this->assertSame(['first order', 'second order', 'single order'], $receivedPayloads); } - public function test_sending_batch_message_over_channel_without_async_publishing_throws(): void + public function test_sending_batch_message_over_channel_without_batch_publishing_throws(): void { $queueName = Uuid::v7()->toRfc4122(); - $messaging = $this->bootstrapPublisher($queueName, asyncPublishing: false); + $messaging = $this->bootstrapPublisher($queueName, highThroughputPublishing: false); $this->expectException(ConfigurationException::class); @@ -135,10 +127,10 @@ public function test_sending_batch_message_over_channel_without_async_publishing ); } - public function test_sending_batch_message_via_publisher_without_async_publishing_throws(): void + public function test_sending_batch_message_via_publisher_without_batch_publishing_throws(): void { $queueName = Uuid::v7()->toRfc4122(); - $messaging = $this->bootstrapPublisher($queueName, asyncPublishing: false); + $messaging = $this->bootstrapPublisher($queueName, highThroughputPublishing: false); $publisher = $messaging->getGateway(MessagePublisher::class); $this->expectException(ConfigurationException::class); @@ -167,7 +159,7 @@ public function handle(string $order, #[Reference(MessagePublisher::class)] Mess ->withSkippedModulePackageNames(ModulePackageList::allPackagesExcept([ModulePackageList::ASYNCHRONOUS_PACKAGE, ModulePackageList::DBAL_PACKAGE])) ->withExtensionObjects([ DbalMessagePublisherConfiguration::create(MessagePublisher::class, $queueName) - ->withAsyncPublishing(), + ->withHighThroughputPublishing(), DbalBackedMessageChannelBuilder::create($queueName), ]), licenceKey: LicenceTesting::VALID_LICENCE, @@ -186,14 +178,14 @@ public function handle(string $order, #[Reference(MessagePublisher::class)] Mess public function test_delayed_entry_of_published_batch_is_delivered_after_delay(): void { $queueName = Uuid::v7()->toRfc4122(); - $messaging = $this->bootstrapPublisher($queueName, asyncPublishing: true); + $messaging = $this->bootstrapPublisher($queueName, highThroughputPublishing: true); $publisher = $messaging->getGateway(MessagePublisher::class); - $publisher->asyncPublish( + $publisher->convertAndSend( BatchMessage::constructEmpty() ->append('immediate order') ->append('delayed order', [MessageHeaders::DELIVERY_DELAY => 3000]) - )->resolve(); + ); $channel = $messaging->getMessageChannel($queueName); $this->assertSame('immediate order', $channel->receive()->getPayload()); @@ -205,14 +197,14 @@ public function test_delayed_entry_of_published_batch_is_delivered_after_delay() public function test_expired_entry_of_published_batch_is_not_delivered(): void { $queueName = Uuid::v7()->toRfc4122(); - $messaging = $this->bootstrapPublisher($queueName, asyncPublishing: true); + $messaging = $this->bootstrapPublisher($queueName, highThroughputPublishing: true); $publisher = $messaging->getGateway(MessagePublisher::class); - $publisher->asyncPublish( + $publisher->convertAndSend( BatchMessage::constructEmpty() ->append('expiring order', [MessageHeaders::TIME_TO_LIVE => 1000]) ->append('kept order') - )->resolve(); + ); sleep(2); @@ -224,15 +216,15 @@ public function test_expired_entry_of_published_batch_is_not_delivered(): void public function test_publishing_after_queue_table_is_dropped_throws(): void { $queueName = Uuid::v7()->toRfc4122(); - $messaging = $this->bootstrapPublisher($queueName, asyncPublishing: true); + $messaging = $this->bootstrapPublisher($queueName, highThroughputPublishing: true); $publisher = $messaging->getGateway(MessagePublisher::class); - $publisher->asyncPublish('first order')->resolve(); + $publisher->send('first order'); $this->getConnection()->executeStatement('DROP TABLE enqueue'); $this->expectException(Exception::class); - $publisher->asyncPublish('order published into missing table'); + $publisher->send('order published into missing table'); } private function receiveWithDeadline(PollableChannel $channel, int $deadlineInSeconds): ?Message @@ -292,11 +284,11 @@ private function bootstrapEcotoneWithChannel(object $orderService, ?string $lice ); } - private function bootstrapPublisher(string $queueName, bool $asyncPublishing): FlowTestSupport + private function bootstrapPublisher(string $queueName, bool $highThroughputPublishing): FlowTestSupport { $publisherConfiguration = DbalMessagePublisherConfiguration::create(MessagePublisher::class, $queueName); - if ($asyncPublishing) { - $publisherConfiguration = $publisherConfiguration->withAsyncPublishing(); + if ($highThroughputPublishing) { + $publisherConfiguration = $publisherConfiguration->withHighThroughputPublishing(); } return EcotoneLite::bootstrapFlowTesting( diff --git a/packages/Dbal/tests/Integration/AsyncPublishingTransactionTest.php b/packages/Dbal/tests/Integration/HighThroughputPublishingTransactionTest.php similarity index 87% rename from packages/Dbal/tests/Integration/AsyncPublishingTransactionTest.php rename to packages/Dbal/tests/Integration/HighThroughputPublishingTransactionTest.php index 71b962ad1..43b925f40 100644 --- a/packages/Dbal/tests/Integration/AsyncPublishingTransactionTest.php +++ b/packages/Dbal/tests/Integration/HighThroughputPublishingTransactionTest.php @@ -7,7 +7,7 @@ use Ecotone\Dbal\Configuration\DbalConfiguration; use Ecotone\Lite\EcotoneLite; use Ecotone\Lite\Test\FlowTestSupport; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Channel\PollableChannel\GlobalPollableChannelConfiguration; use Ecotone\Messaging\Channel\SimpleMessageChannelBuilder; use Ecotone\Messaging\Config\ModulePackageList; @@ -16,7 +16,7 @@ use Ecotone\Modelling\AggregateNotFoundException; use Enqueue\Dbal\DbalConnectionFactory; use Test\Ecotone\Dbal\DbalMessagingTestCase; -use Test\Ecotone\Dbal\Fixture\AsyncPublishing\AsyncPublishingTestChannelBuilder; +use Test\Ecotone\Dbal\Fixture\HighThroughputPublishing\HighThroughputTestChannelBuilder; use Test\Ecotone\Dbal\Fixture\ORM\AsynchronousEventHandler\NotificationService; use Test\Ecotone\Dbal\Fixture\ORM\Person\Person; use Test\Ecotone\Dbal\Fixture\ORM\Person\RegisterPerson; @@ -25,12 +25,12 @@ * licence Apache-2.0 * @internal */ -final class AsyncPublishingTransactionTest extends DbalMessagingTestCase +final class HighThroughputPublishingTransactionTest extends DbalMessagingTestCase { public function test_successful_delivery_confirmations_commit_database_transaction(): void { $ecotoneLite = $this->bootstrapEcotone( - [AsyncPublishingTestChannelBuilder::create('notifications')], + [HighThroughputTestChannelBuilder::create('notifications')], [] ); @@ -43,7 +43,7 @@ public function test_successful_delivery_confirmations_commit_database_transacti public function test_failed_delivery_confirmation_rolls_back_database_transaction(): void { $ecotoneLite = $this->bootstrapEcotone( - [AsyncPublishingTestChannelBuilder::create('notifications', deliveryFailureReason: 'broker not available')], + [HighThroughputTestChannelBuilder::create('notifications', deliveryFailureReason: 'broker not available')], [] ); @@ -64,7 +64,7 @@ public function test_failed_delivery_routed_to_error_channel_commits_database_tr { $ecotoneLite = $this->bootstrapEcotone( [ - AsyncPublishingTestChannelBuilder::create('notifications', deliveryFailureReason: 'broker not available'), + HighThroughputTestChannelBuilder::create('notifications', deliveryFailureReason: 'broker not available'), SimpleMessageChannelBuilder::createQueueChannel('failure_channel'), ], [GlobalPollableChannelConfiguration::createWithDefaults()->withErrorChannel('failure_channel')] diff --git a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/ConfirmedDelivery.php b/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/ConfirmedDelivery.php deleted file mode 100644 index f354fb9aa..000000000 --- a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/ConfirmedDelivery.php +++ /dev/null @@ -1,21 +0,0 @@ -registerGatewayBuilder( - GatewayProxyBuilder::create($publisherReferenceName, MessagePublisher::class, 'asyncPublish', $asyncPublishRequestChannel) + GatewayProxyBuilder::create($publisherReferenceName, MessagePublisher::class, 'publishDeferred', $publishDeferredRequestChannel) ->withParameterConverters([ GatewayPayloadBuilder::create('data'), GatewayHeaderBuilder::create('sourceMediaType', MessageHeaders::CONTENT_TYPE), GatewayHeadersBuilder::create('metadata'), ]) ) - ->registerMessageChannel(SimpleMessageChannelBuilder::createDirectMessageChannel($asyncPublishRequestChannel)) + ->registerMessageChannel(SimpleMessageChannelBuilder::createDirectMessageChannel($publishDeferredRequestChannel)) ->registerMessageHandler( ServiceActivatorBuilder::createWithDefinition( - new Definition(AsyncPublishingGateway::class, [ + new Definition(DeferredPublishingGateway::class, [ $publisherReferenceName, - $asyncPublishingEnabled, + $nonBlockingConfirmationEnabled, new Reference(ConfiguredMessagingSystem::class), - new Reference(AsyncPublishingRegistry::class), + new Reference(PendingDeliveryRegistry::class), ]), 'publish' ) - ->withInputChannelName($asyncPublishRequestChannel) - ->withEndpointId($asyncPublishRequestChannel . '.endpoint') + ->withInputChannelName($publishDeferredRequestChannel) + ->withEndpointId($publishDeferredRequestChannel . '.endpoint') ); } } diff --git a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/Config/AsyncPublishingModule.php b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/Config/DeliveryConfirmationModule.php similarity index 80% rename from packages/Ecotone/src/Messaging/Channel/AsyncPublishing/Config/AsyncPublishingModule.php rename to packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/Config/DeliveryConfirmationModule.php index 55e089750..1c1488167 100644 --- a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/Config/AsyncPublishingModule.php +++ b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/Config/DeliveryConfirmationModule.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace Ecotone\Messaging\Channel\AsyncPublishing\Config; +namespace Ecotone\Messaging\Channel\DeliveryConfirmation\Config; use Ecotone\AnnotationFinder\AnnotationFinder; use Ecotone\Messaging\Attribute\AsynchronousRunningEndpoint; use Ecotone\Messaging\Attribute\ModuleAnnotation; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingWaiterInterceptor; +use Ecotone\Messaging\Channel\DeliveryConfirmation\DeliveryConfirmationInterceptor; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDeliveryRegistry; use Ecotone\Messaging\Channel\PollableChannel\GlobalPollableChannelConfiguration; use Ecotone\Messaging\Channel\PollableChannel\PollableChannelConfiguration; use Ecotone\Messaging\Config\Annotation\AnnotationModule; @@ -30,7 +30,7 @@ /** * licence Enterprise */ -final class AsyncPublishingModule extends NoExternalConfigurationModule implements AnnotationModule +final class DeliveryConfirmationModule extends NoExternalConfigurationModule implements AnnotationModule { public static function create(AnnotationFinder $annotationRegistrationService, InterfaceToCallRegistry $interfaceToCallRegistry): static { @@ -46,9 +46,9 @@ public function prepare(Configuration $messagingConfiguration, array $extensionO } $messagingConfiguration->registerServiceDefinition( - AsyncPublishingWaiterInterceptor::class, - new Definition(AsyncPublishingWaiterInterceptor::class, [ - new Reference(AsyncPublishingRegistry::class), + DeliveryConfirmationInterceptor::class, + new Definition(DeliveryConfirmationInterceptor::class, [ + new Reference(PendingDeliveryRegistry::class), $errorChannels, $globalPollableChannelConfiguration->getErrorChannelName(), new Reference(ErrorChannelService::class), @@ -57,9 +57,9 @@ public function prepare(Configuration $messagingConfiguration, array $extensionO ); $messagingConfiguration->registerAroundMethodInterceptor( AroundInterceptorBuilder::create( - AsyncPublishingWaiterInterceptor::class, - $interfaceToCallRegistry->getFor(AsyncPublishingWaiterInterceptor::class, 'await'), - Precedence::ASYNC_PUBLISHING_AWAIT_PRECEDENCE, + DeliveryConfirmationInterceptor::class, + $interfaceToCallRegistry->getFor(DeliveryConfirmationInterceptor::class, 'await'), + Precedence::DELIVERY_CONFIRMATION_PRECEDENCE, CommandBus::class . '||' . AsynchronousRunningEndpoint::class, ) ); diff --git a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/AsyncPublishingGateway.php b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/DeferredPublishingGateway.php similarity index 68% rename from packages/Ecotone/src/Messaging/Channel/AsyncPublishing/AsyncPublishingGateway.php rename to packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/DeferredPublishingGateway.php index d89645210..516002561 100644 --- a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/AsyncPublishingGateway.php +++ b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/DeferredPublishingGateway.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Ecotone\Messaging\Channel\AsyncPublishing; +namespace Ecotone\Messaging\Channel\DeliveryConfirmation; use Ecotone\Messaging\BatchMessage; use Ecotone\Messaging\Config\ConfiguredMessagingSystem; @@ -14,20 +14,20 @@ /** * licence Enterprise */ -final class AsyncPublishingGateway +final class DeferredPublishingGateway { public function __construct( private string $publisherReference, - private bool $asyncPublishingEnabled, + private bool $nonBlockingConfirmationEnabled, private ConfiguredMessagingSystem $configuredMessagingSystem, - private AsyncPublishingRegistry $asyncPublishingRegistry, + private PendingDeliveryRegistry $pendingDeliveryRegistry, ) { } public function publish(Message $message): Future { - if (! $this->asyncPublishingEnabled) { - throw PublishingFailedException::publisherNotConfiguredForAsyncPublishing($this->publisherReference); + if (! $this->nonBlockingConfirmationEnabled) { + throw PublishingFailedException::publisherNotConfiguredForDeferredPublishing($this->publisherReference); } $payload = $message->getPayload(); @@ -35,13 +35,13 @@ public function publish(Message $message): Future return DeliveryFuture::forPendingDeliveries([]); } - $scopeWasActive = $this->asyncPublishingRegistry->isScopeActive(); + $scopeWasActive = $this->pendingDeliveryRegistry->isScopeActive(); if (! $scopeWasActive) { - $this->asyncPublishingRegistry->openScope(); + $this->pendingDeliveryRegistry->openScope(); } try { - $collectionPoint = $this->asyncPublishingRegistry->collectionPoint(); + $collectionPoint = $this->pendingDeliveryRegistry->collectionPoint(); $this->configuredMessagingSystem->getMessageChannelByName($this->publisherReference)->send( MessageBuilder::fromMessage($message) @@ -50,18 +50,18 @@ public function publish(Message $message): Future ->build() ); - $pendingDeliveries = $this->asyncPublishingRegistry->registeredSince($collectionPoint); + $pendingDeliveries = $this->pendingDeliveryRegistry->registeredSince($collectionPoint); if (! $scopeWasActive) { - $this->asyncPublishingRegistry->markRegisteredSinceAsPublisherOwned($collectionPoint); + $this->pendingDeliveryRegistry->markRegisteredSinceAsPublisherOwned($collectionPoint); } } finally { if (! $scopeWasActive) { - $this->asyncPublishingRegistry->closeScope(); + $this->pendingDeliveryRegistry->closeScope(); } } if ($pendingDeliveries === []) { - throw PublishingFailedException::publisherNotConfiguredForAsyncPublishing($this->publisherReference); + throw PublishingFailedException::publisherNotConfiguredForDeferredPublishing($this->publisherReference); } return DeliveryFuture::forPendingDeliveries($pendingDeliveries); diff --git a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/AsyncPublishingWaiterInterceptor.php b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/DeliveryConfirmationInterceptor.php similarity index 86% rename from packages/Ecotone/src/Messaging/Channel/AsyncPublishing/AsyncPublishingWaiterInterceptor.php rename to packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/DeliveryConfirmationInterceptor.php index 6de7aca01..5bb702329 100644 --- a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/AsyncPublishingWaiterInterceptor.php +++ b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/DeliveryConfirmationInterceptor.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Ecotone\Messaging\Channel\AsyncPublishing; +namespace Ecotone\Messaging\Channel\DeliveryConfirmation; use Ecotone\Messaging\BatchMessage; use Ecotone\Messaging\Config\ConfiguredMessagingSystem; @@ -14,13 +14,13 @@ /** * licence Enterprise */ -final class AsyncPublishingWaiterInterceptor +final class DeliveryConfirmationInterceptor { /** * @param array $errorChannels */ public function __construct( - private AsyncPublishingRegistry $asyncPublishingRegistry, + private PendingDeliveryRegistry $pendingDeliveryRegistry, private array $errorChannels, private ?string $globalErrorChannelName, private ErrorChannelService $errorChannelService, @@ -30,26 +30,26 @@ public function __construct( public function await(MethodInvocation $methodInvocation): mixed { - if ($this->asyncPublishingRegistry->isScopeActive()) { + if ($this->pendingDeliveryRegistry->isScopeActive()) { return $methodInvocation->proceed(); } - $this->asyncPublishingRegistry->openScope(); + $this->pendingDeliveryRegistry->openScope(); try { $result = $methodInvocation->proceed(); - $deliveryResult = $this->asyncPublishingRegistry->awaitAll(); + $deliveryResult = $this->pendingDeliveryRegistry->awaitAll(); if (! $deliveryResult->isSuccessful()) { $unroutedFailedDeliveries = $this->handleFailedDeliveries($deliveryResult->getFailedDeliveries()); - $errorChannelDeliveryResult = $this->asyncPublishingRegistry->awaitAll(); + $errorChannelDeliveryResult = $this->pendingDeliveryRegistry->awaitAll(); $remainingFailedDeliveries = array_merge($unroutedFailedDeliveries, $errorChannelDeliveryResult->getFailedDeliveries()); if ($remainingFailedDeliveries !== []) { throw PublishingFailedException::withFailedDeliveries($remainingFailedDeliveries); } } } finally { - $this->asyncPublishingRegistry->closeScope(); + $this->pendingDeliveryRegistry->closeScope(); } return $result; diff --git a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/DeliveryFuture.php b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/DeliveryFuture.php similarity index 97% rename from packages/Ecotone/src/Messaging/Channel/AsyncPublishing/DeliveryFuture.php rename to packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/DeliveryFuture.php index e73f0dfae..202ca1456 100644 --- a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/DeliveryFuture.php +++ b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/DeliveryFuture.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Ecotone\Messaging\Channel\AsyncPublishing; +namespace Ecotone\Messaging\Channel\DeliveryConfirmation; use Ecotone\Messaging\Future; use Throwable; diff --git a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/DeliveryResult.php b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/DeliveryResult.php similarity index 93% rename from packages/Ecotone/src/Messaging/Channel/AsyncPublishing/DeliveryResult.php rename to packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/DeliveryResult.php index 42542df25..79a469645 100644 --- a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/DeliveryResult.php +++ b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/DeliveryResult.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Ecotone\Messaging\Channel\AsyncPublishing; +namespace Ecotone\Messaging\Channel\DeliveryConfirmation; /** * licence Enterprise diff --git a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/FailedDelivery.php b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/FailedDelivery.php similarity index 90% rename from packages/Ecotone/src/Messaging/Channel/AsyncPublishing/FailedDelivery.php rename to packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/FailedDelivery.php index d62fdbbb0..5d1de86aa 100644 --- a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/FailedDelivery.php +++ b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/FailedDelivery.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Ecotone\Messaging\Channel\AsyncPublishing; +namespace Ecotone\Messaging\Channel\DeliveryConfirmation; use Ecotone\Messaging\Message; diff --git a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/PendingDelivery.php b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/PendingDelivery.php similarity index 76% rename from packages/Ecotone/src/Messaging/Channel/AsyncPublishing/PendingDelivery.php rename to packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/PendingDelivery.php index 444704b82..d4a3fec86 100644 --- a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/PendingDelivery.php +++ b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/PendingDelivery.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Ecotone\Messaging\Channel\AsyncPublishing; +namespace Ecotone\Messaging\Channel\DeliveryConfirmation; /** * licence Enterprise diff --git a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/AsyncPublishingRegistry.php b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/PendingDeliveryRegistry.php similarity index 98% rename from packages/Ecotone/src/Messaging/Channel/AsyncPublishing/AsyncPublishingRegistry.php rename to packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/PendingDeliveryRegistry.php index bedf05ed7..8005c2551 100644 --- a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/AsyncPublishingRegistry.php +++ b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/PendingDeliveryRegistry.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Ecotone\Messaging\Channel\AsyncPublishing; +namespace Ecotone\Messaging\Channel\DeliveryConfirmation; use Ecotone\Messaging\Handler\Logger\LoggingGateway; use Throwable; @@ -11,7 +11,7 @@ /** * licence Enterprise */ -final class AsyncPublishingRegistry +final class PendingDeliveryRegistry { /** @var array */ private array $pendingDeliveries = []; diff --git a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/PublishingFailedException.php b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/PublishingFailedException.php similarity index 76% rename from packages/Ecotone/src/Messaging/Channel/AsyncPublishing/PublishingFailedException.php rename to packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/PublishingFailedException.php index ddd84d828..5d0cac009 100644 --- a/packages/Ecotone/src/Messaging/Channel/AsyncPublishing/PublishingFailedException.php +++ b/packages/Ecotone/src/Messaging/Channel/DeliveryConfirmation/PublishingFailedException.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Ecotone\Messaging\Channel\AsyncPublishing; +namespace Ecotone\Messaging\Channel\DeliveryConfirmation; use Ecotone\Messaging\MessagingException; @@ -34,10 +34,10 @@ public static function withFailedDeliveries(array $failedDeliveries): self /** @var FailedDelivery[] */ private array $failedDeliveries = []; - public static function publisherNotConfiguredForAsyncPublishing(string $publisherReference): self + public static function publisherNotConfiguredForDeferredPublishing(string $publisherReference): self { return new self(sprintf( - 'Message Publisher `%s` is not configured for asynchronous publishing. Enable async publishing on the publisher configuration to make use of asyncPublish.', + 'Message Publisher `%s` is not configured for non blocking confirmation. Call withHighThroughputPublishing() on the publisher configuration to make use of publishDeferred.', $publisherReference, )); } diff --git a/packages/Ecotone/src/Messaging/Channel/PollableChannel/SendRetries/SendRetryChannelInterceptor.php b/packages/Ecotone/src/Messaging/Channel/PollableChannel/SendRetries/SendRetryChannelInterceptor.php index bf27eb9fb..6268dae56 100644 --- a/packages/Ecotone/src/Messaging/Channel/PollableChannel/SendRetries/SendRetryChannelInterceptor.php +++ b/packages/Ecotone/src/Messaging/Channel/PollableChannel/SendRetries/SendRetryChannelInterceptor.php @@ -6,9 +6,9 @@ use Ecotone\Messaging\BatchMessage; use Ecotone\Messaging\Channel\AbstractChannelInterceptor; -use Ecotone\Messaging\Channel\AsyncPublishing\FailedDelivery; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; use Ecotone\Messaging\Channel\ChannelInterceptor; +use Ecotone\Messaging\Channel\DeliveryConfirmation\FailedDelivery; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Config\ConfiguredMessagingSystem; use Ecotone\Messaging\Handler\Gateway\ErrorChannelService; use Ecotone\Messaging\Handler\Recoverability\RetryTemplate; diff --git a/packages/Ecotone/src/Messaging/Config/Container/Compiler/RegisterSingletonMessagingServices.php b/packages/Ecotone/src/Messaging/Config/Container/Compiler/RegisterSingletonMessagingServices.php index b5ffd4eae..74779df18 100644 --- a/packages/Ecotone/src/Messaging/Config/Container/Compiler/RegisterSingletonMessagingServices.php +++ b/packages/Ecotone/src/Messaging/Config/Container/Compiler/RegisterSingletonMessagingServices.php @@ -3,7 +3,7 @@ namespace Ecotone\Messaging\Config\Container\Compiler; use Ecotone\EventSourcing\Mapping\EventMapper; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDeliveryRegistry; use Ecotone\Messaging\Config\ConfiguredMessagingSystem; use Ecotone\Messaging\Config\Container\ChannelResolverWithContainer; use Ecotone\Messaging\Config\Container\ContainerBuilder; @@ -67,7 +67,7 @@ public function process(ContainerBuilder $builder): void $this->registerDefault($builder, ConfiguredMessagingSystem::class, new Definition(MessagingSystemContainer::class, [new Reference(ContainerInterface::class), [], []])); $this->registerDefault($builder, EventMapper::class, new Definition(EventMapper::class, factory: 'createEmpty')); $this->registerDefault($builder, LicenceDecider::class, new Definition(LicenceDecider::class, [$this->serviceConfiguration->isRunningForEnterprise()])); - $this->registerDefault($builder, AsyncPublishingRegistry::class, new Definition(AsyncPublishingRegistry::class, [new Reference(LoggingGateway::class)])); + $this->registerDefault($builder, PendingDeliveryRegistry::class, new Definition(PendingDeliveryRegistry::class, [new Reference(LoggingGateway::class)])); } private function registerDefault(ContainerBuilder $builder, string $id, Definition|Reference $definition): void diff --git a/packages/Ecotone/src/Messaging/Config/ModuleClassList.php b/packages/Ecotone/src/Messaging/Config/ModuleClassList.php index 227e45b5e..89aaa39cb 100644 --- a/packages/Ecotone/src/Messaging/Config/ModuleClassList.php +++ b/packages/Ecotone/src/Messaging/Config/ModuleClassList.php @@ -27,8 +27,8 @@ use Ecotone\Kafka\Configuration\KafkaModule; use Ecotone\Laravel\Config\LaravelConnectionModule; use Ecotone\Lite\Test\Configuration\EcotoneTestSupportModule; -use Ecotone\Messaging\Channel\AsyncPublishing\Config\AsyncPublishingModule; use Ecotone\Messaging\Channel\Collector\Config\CollectorModule; +use Ecotone\Messaging\Channel\DeliveryConfirmation\Config\DeliveryConfirmationModule; use Ecotone\Messaging\Channel\DynamicChannel\Config\DynamicMessageChannelModule; use Ecotone\Messaging\Channel\Manager\ChannelSetupModule; use Ecotone\Messaging\Channel\PollableChannel\InMemory\InMemoryQueueAcknowledgeModule; @@ -110,7 +110,7 @@ class ModuleClassList RouterModule::class, ScheduledModule::class, CollectorModule::class, - AsyncPublishingModule::class, + DeliveryConfirmationModule::class, ChannelSetupModule::class, SerializerModule::class, ServiceActivatorModule::class, diff --git a/packages/Ecotone/src/Messaging/MessagePublisher.php b/packages/Ecotone/src/Messaging/MessagePublisher.php index 2ffec2626..bcec3eb21 100644 --- a/packages/Ecotone/src/Messaging/MessagePublisher.php +++ b/packages/Ecotone/src/Messaging/MessagePublisher.php @@ -19,5 +19,10 @@ public function convertAndSend(object|array $data): void; public function convertAndSendWithMetadata(object|array $data, array $metadata): void; - public function asyncPublish(mixed $data, string $sourceMediaType = MediaType::APPLICATION_X_PHP, array $metadata = []): Future; + /** + * Publishes without blocking on delivery confirmation. + * Requires High Throughput Publishing with non blocking confirmation enabled on the publisher. + * Confirmation is awaited on Future::resolve(), or at the latest when the surrounding Command Bus or asynchronous endpoint finishes. + */ + public function publishDeferred(mixed $data, string $sourceMediaType = MediaType::APPLICATION_X_PHP, array $metadata = []): Future; } diff --git a/packages/Ecotone/src/Messaging/Precedence.php b/packages/Ecotone/src/Messaging/Precedence.php index 2744a9945..e6f3992d9 100644 --- a/packages/Ecotone/src/Messaging/Precedence.php +++ b/packages/Ecotone/src/Messaging/Precedence.php @@ -61,12 +61,12 @@ interface Precedence /** * Awaits delivery confirmations of asynchronously published messages before transaction commits */ - public const ASYNC_PUBLISHING_AWAIT_PRECEDENCE = self::DATABASE_TRANSACTION_PRECEDENCE + 1; + public const DELIVERY_CONFIRMATION_PRECEDENCE = self::DATABASE_TRANSACTION_PRECEDENCE + 1; /** * Collects messages to be sent to asynchronous channels. */ - public const COLLECTOR_SENDER_PRECEDENCE = self::ASYNC_PUBLISHING_AWAIT_PRECEDENCE + 1; + public const COLLECTOR_SENDER_PRECEDENCE = self::DELIVERY_CONFIRMATION_PRECEDENCE + 1; public const DATABASE_OBJECT_MANAGER_PRECEDENCE = self::COLLECTOR_SENDER_PRECEDENCE + 1; diff --git a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/AsyncOrderForwarder.php b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/AsyncOrderForwarder.php similarity index 90% rename from packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/AsyncOrderForwarder.php rename to packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/AsyncOrderForwarder.php index 6dadbde4e..7904215a9 100644 --- a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/AsyncOrderForwarder.php +++ b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/AsyncOrderForwarder.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Fixture\AsyncPublishing; +namespace Test\Ecotone\Messaging\Fixture\HighThroughputPublishing; use Ecotone\Messaging\Attribute\Asynchronous; use Ecotone\Modelling\Attribute\EventHandler; diff --git a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/AsyncOrderSubscriber.php b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/AsyncOrderSubscriber.php similarity index 90% rename from packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/AsyncOrderSubscriber.php rename to packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/AsyncOrderSubscriber.php index 8926ce032..23b313207 100644 --- a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/AsyncOrderSubscriber.php +++ b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/AsyncOrderSubscriber.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Fixture\AsyncPublishing; +namespace Test\Ecotone\Messaging\Fixture\HighThroughputPublishing; use Ecotone\Messaging\Attribute\Asynchronous; use Ecotone\Modelling\Attribute\EventHandler; diff --git a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/FakeTransactionInterceptor.php b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/FakeTransactionInterceptor.php similarity index 93% rename from packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/FakeTransactionInterceptor.php rename to packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/FakeTransactionInterceptor.php index 01edb2d77..12de59c85 100644 --- a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/FakeTransactionInterceptor.php +++ b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/FakeTransactionInterceptor.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Fixture\AsyncPublishing; +namespace Test\Ecotone\Messaging\Fixture\HighThroughputPublishing; use Ecotone\Messaging\Handler\Processor\MethodInvoker\MethodInvocation; use Throwable; diff --git a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/FakeTransactionModule.php b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/FakeTransactionModule.php similarity index 97% rename from packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/FakeTransactionModule.php rename to packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/FakeTransactionModule.php index b241c01dd..30740203d 100644 --- a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/FakeTransactionModule.php +++ b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/FakeTransactionModule.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Fixture\AsyncPublishing; +namespace Test\Ecotone\Messaging\Fixture\HighThroughputPublishing; use Ecotone\AnnotationFinder\AnnotationFinder; use Ecotone\Messaging\Attribute\AsynchronousRunningEndpoint; diff --git a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/InMemoryAsyncOutboundAdapter.php b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/InMemoryHighThroughputOutboundAdapter.php similarity index 88% rename from packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/InMemoryAsyncOutboundAdapter.php rename to packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/InMemoryHighThroughputOutboundAdapter.php index 23767a13e..5334043c5 100644 --- a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/InMemoryAsyncOutboundAdapter.php +++ b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/InMemoryHighThroughputOutboundAdapter.php @@ -2,18 +2,18 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Fixture\AsyncPublishing; +namespace Test\Ecotone\Messaging\Fixture\HighThroughputPublishing; use Ecotone\Messaging\Attribute\Parameter\Reference; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDeliveryRegistry; use Ecotone\Messaging\Message; use Ecotone\Messaging\Support\MessageBuilder; /** * licence Apache-2.0 */ -final class InMemoryAsyncOutboundAdapter +final class InMemoryHighThroughputOutboundAdapter { /** @var Message[] */ private array $sentMessages = []; @@ -27,7 +27,7 @@ final class InMemoryAsyncOutboundAdapter private bool $registersPendingDeliveries = true; - public function handle(Message $message, #[Reference] AsyncPublishingRegistry $asyncPublishingRegistry): void + public function handle(Message $message, #[Reference] PendingDeliveryRegistry $pendingDeliveryRegistry): void { $this->sentMessages[] = $message; @@ -37,7 +37,7 @@ public function handle(Message $message, #[Reference] AsyncPublishingRegistry $a $pendingDelivery = new InMemoryPendingDelivery($message, $this->resolveFailureReason($message), failedMessages: $this->resolveFailedMessages($message)); $this->pendingDeliveries[] = $pendingDelivery; - $asyncPublishingRegistry->register(InMemoryAsyncPublisherModule::PUBLISHER_REFERENCE, $pendingDelivery); + $pendingDeliveryRegistry->register(InMemoryHighThroughputPublisherModule::PUBLISHER_REFERENCE, $pendingDelivery); } /** diff --git a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/InMemoryAsyncPublisherModule.php b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/InMemoryHighThroughputPublisherModule.php similarity index 74% rename from packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/InMemoryAsyncPublisherModule.php rename to packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/InMemoryHighThroughputPublisherModule.php index 4e3a3ebb0..fa29183ca 100644 --- a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/InMemoryAsyncPublisherModule.php +++ b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/InMemoryHighThroughputPublisherModule.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Fixture\AsyncPublishing; +namespace Test\Ecotone\Messaging\Fixture\HighThroughputPublishing; use Ecotone\AnnotationFinder\AnnotationFinder; use Ecotone\Messaging\Attribute\ModuleAnnotation; -use Ecotone\Messaging\Channel\AsyncPublishing\Config\AsyncPublishGatewayRegistration; +use Ecotone\Messaging\Channel\DeliveryConfirmation\Config\DeferredPublishingGatewayRegistration; use Ecotone\Messaging\Channel\SimpleMessageChannelBuilder; use Ecotone\Messaging\Config\Annotation\AnnotationModule; use Ecotone\Messaging\Config\Annotation\ModuleConfiguration\NoExternalConfigurationModule; @@ -20,9 +20,9 @@ /** * licence Apache-2.0 */ -final class InMemoryAsyncPublisherModule extends NoExternalConfigurationModule implements AnnotationModule +final class InMemoryHighThroughputPublisherModule extends NoExternalConfigurationModule implements AnnotationModule { - public const PUBLISHER_REFERENCE = 'inMemoryAsyncPublisher'; + public const PUBLISHER_REFERENCE = 'inMemoryHighThroughputPublisher'; public static function create(AnnotationFinder $annotationRegistrationService, InterfaceToCallRegistry $interfaceToCallRegistry): static { @@ -33,12 +33,12 @@ public function prepare(Configuration $messagingConfiguration, array $extensionO { $publisherReference = self::PUBLISHER_REFERENCE; - AsyncPublishGatewayRegistration::registerFor($messagingConfiguration, $publisherReference); + DeferredPublishingGatewayRegistration::registerFor($messagingConfiguration, $publisherReference); $messagingConfiguration ->registerMessageChannel(SimpleMessageChannelBuilder::createDirectMessageChannel($publisherReference)) ->registerMessageHandler( - ServiceActivatorBuilder::create(InMemoryAsyncOutboundAdapter::class, 'handle') + ServiceActivatorBuilder::create(InMemoryHighThroughputOutboundAdapter::class, 'handle') ->withInputChannelName($publisherReference) ->withEndpointId($publisherReference . '.handler') ); diff --git a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/InMemoryAsyncPublishingChannel.php b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/InMemoryHighThroughputPublishingChannel.php similarity index 88% rename from packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/InMemoryAsyncPublishingChannel.php rename to packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/InMemoryHighThroughputPublishingChannel.php index f4ec8cb11..c2a8e1541 100644 --- a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/InMemoryAsyncPublishingChannel.php +++ b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/InMemoryHighThroughputPublishingChannel.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Fixture\AsyncPublishing; +namespace Test\Ecotone\Messaging\Fixture\HighThroughputPublishing; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; use Ecotone\Messaging\Channel\BatchSupportingMessageChannel; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDeliveryRegistry; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Endpoint\PollingMetadata; use Ecotone\Messaging\Message; use Ecotone\Messaging\PollableChannel; @@ -16,7 +16,7 @@ /** * licence Apache-2.0 */ -final class InMemoryAsyncPublishingChannel implements PollableChannel, BatchSupportingMessageChannel +final class InMemoryHighThroughputPublishingChannel implements PollableChannel, BatchSupportingMessageChannel { /** @var Message[] */ private array $queue = []; @@ -27,7 +27,7 @@ final class InMemoryAsyncPublishingChannel implements PollableChannel, BatchSupp public function __construct( private string $channelName, - private AsyncPublishingRegistry $asyncPublishingRegistry, + private PendingDeliveryRegistry $pendingDeliveryRegistry, private OperationsLog $operationsLog, ) { } @@ -56,7 +56,7 @@ public function send(Message $message): void failedMessages: $this->resolveFailedMessages($message), ); - if (! $this->asyncPublishingRegistry->isScopeActive()) { + if (! $this->pendingDeliveryRegistry->isScopeActive()) { $deliveryResult = $pendingDelivery->awaitDelivery(); if (! $deliveryResult->isSuccessful()) { throw PublishingFailedException::withFailedDeliveries($deliveryResult->getFailedDeliveries()); @@ -65,7 +65,7 @@ public function send(Message $message): void return; } - $this->asyncPublishingRegistry->register($this->channelName, $pendingDelivery); + $this->pendingDeliveryRegistry->register($this->channelName, $pendingDelivery); } public function receive(): ?Message diff --git a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/InMemoryAsyncPublishingChannelBuilder.php b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/InMemoryHighThroughputPublishingChannelBuilder.php similarity index 71% rename from packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/InMemoryAsyncPublishingChannelBuilder.php rename to packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/InMemoryHighThroughputPublishingChannelBuilder.php index edf9fbbf0..78d1aee32 100644 --- a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/InMemoryAsyncPublishingChannelBuilder.php +++ b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/InMemoryHighThroughputPublishingChannelBuilder.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Fixture\AsyncPublishing; +namespace Test\Ecotone\Messaging\Fixture\HighThroughputPublishing; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDeliveryRegistry; use Ecotone\Messaging\Channel\MessageChannelBuilder; use Ecotone\Messaging\Config\Container\Definition; use Ecotone\Messaging\Config\Container\MessagingContainerBuilder; @@ -13,7 +13,7 @@ /** * licence Apache-2.0 */ -final class InMemoryAsyncPublishingChannelBuilder implements MessageChannelBuilder +final class InMemoryHighThroughputPublishingChannelBuilder implements MessageChannelBuilder { private function __construct(private string $channelName) { @@ -41,9 +41,9 @@ public function isStreamingChannel(): bool public function compile(MessagingContainerBuilder $builder): Definition|Reference { - return new Definition(InMemoryAsyncPublishingChannel::class, [ + return new Definition(InMemoryHighThroughputPublishingChannel::class, [ $this->channelName, - new Reference(AsyncPublishingRegistry::class), + new Reference(PendingDeliveryRegistry::class), new Reference(OperationsLog::class), ]); } diff --git a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/InMemoryPendingDelivery.php b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/InMemoryPendingDelivery.php similarity index 86% rename from packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/InMemoryPendingDelivery.php rename to packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/InMemoryPendingDelivery.php index 03c733022..35ab5746a 100644 --- a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/InMemoryPendingDelivery.php +++ b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/InMemoryPendingDelivery.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Fixture\AsyncPublishing; +namespace Test\Ecotone\Messaging\Fixture\HighThroughputPublishing; -use Ecotone\Messaging\Channel\AsyncPublishing\DeliveryResult; -use Ecotone\Messaging\Channel\AsyncPublishing\FailedDelivery; -use Ecotone\Messaging\Channel\AsyncPublishing\PendingDelivery; +use Ecotone\Messaging\Channel\DeliveryConfirmation\DeliveryResult; +use Ecotone\Messaging\Channel\DeliveryConfirmation\FailedDelivery; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDelivery; use Ecotone\Messaging\Message; use RuntimeException; diff --git a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/OperationsLog.php b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/OperationsLog.php similarity index 85% rename from packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/OperationsLog.php rename to packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/OperationsLog.php index 2813100db..d75e7ad37 100644 --- a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/OperationsLog.php +++ b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/OperationsLog.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Fixture\AsyncPublishing; +namespace Test\Ecotone\Messaging\Fixture\HighThroughputPublishing; /** * licence Apache-2.0 diff --git a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/OrderRequestReceived.php b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/OrderRequestReceived.php similarity index 71% rename from packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/OrderRequestReceived.php rename to packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/OrderRequestReceived.php index 8d576a417..c6cc0351a 100644 --- a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/OrderRequestReceived.php +++ b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/OrderRequestReceived.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Fixture\AsyncPublishing; +namespace Test\Ecotone\Messaging\Fixture\HighThroughputPublishing; /** * licence Apache-2.0 diff --git a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/OrderService.php b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/OrderService.php similarity index 92% rename from packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/OrderService.php rename to packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/OrderService.php index 454a16c5d..9f37a65ba 100644 --- a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/OrderService.php +++ b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/OrderService.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Fixture\AsyncPublishing; +namespace Test\Ecotone\Messaging\Fixture\HighThroughputPublishing; use Ecotone\Modelling\Attribute\CommandHandler; use Ecotone\Modelling\CommandBus; diff --git a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/OrderWasPlaced.php b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/OrderWasPlaced.php similarity index 70% rename from packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/OrderWasPlaced.php rename to packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/OrderWasPlaced.php index c10c29e5c..9b6392c6b 100644 --- a/packages/Ecotone/tests/Messaging/Fixture/AsyncPublishing/OrderWasPlaced.php +++ b/packages/Ecotone/tests/Messaging/Fixture/HighThroughputPublishing/OrderWasPlaced.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Fixture\AsyncPublishing; +namespace Test\Ecotone\Messaging\Fixture\HighThroughputPublishing; /** * licence Apache-2.0 diff --git a/packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/MessagePublisherAsyncPublishTest.php b/packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/MessagePublisherAsyncPublishTest.php deleted file mode 100644 index 2272b17d5..000000000 --- a/packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/MessagePublisherAsyncPublishTest.php +++ /dev/null @@ -1,112 +0,0 @@ -bootstrapPublisher($outboundAdapter); - - $future = $publisher->asyncPublish('order was placed'); - - $this->assertSame(['order was placed'], $outboundAdapter->getSentPayloads()); - $this->assertSame(0, $outboundAdapter->awaitedDeliveriesCount()); - - $future->resolve(); - - $this->assertSame(1, $outboundAdapter->awaitedDeliveriesCount()); - } - - public function test_resolving_future_twice_awaits_delivery_only_once(): void - { - $outboundAdapter = new InMemoryAsyncOutboundAdapter(); - $publisher = $this->bootstrapPublisher($outboundAdapter); - - $future = $publisher->asyncPublish('order was placed'); - $future->resolve(); - $future->resolve(); - - $this->assertSame(1, $outboundAdapter->totalAwaitCalls()); - } - - public function test_resolving_future_throws_when_delivery_failed(): void - { - $outboundAdapter = new InMemoryAsyncOutboundAdapter(); - $outboundAdapter->failDeliveriesWith('broker rejected message'); - $publisher = $this->bootstrapPublisher($outboundAdapter); - - $future = $publisher->asyncPublish('order was placed'); - - $this->expectException(PublishingFailedException::class); - - $future->resolve(); - } - - public function test_async_publish_on_synchronous_publisher_throws_clear_exception(): void - { - $outboundAdapter = new InMemoryAsyncOutboundAdapter(); - $outboundAdapter->actAsSynchronousPublisher(); - $publisher = $this->bootstrapPublisher($outboundAdapter); - - $this->expectException(PublishingFailedException::class); - $this->expectExceptionMessageMatches('/not configured for asynchronous publishing/'); - - $publisher->asyncPublish('order was placed'); - } - - public function test_metadata_passed_to_async_publish_lands_on_published_message(): void - { - $outboundAdapter = new InMemoryAsyncOutboundAdapter(); - $publisher = $this->bootstrapPublisher($outboundAdapter); - - $publisher->asyncPublish('order was placed', metadata: ['orderId' => '123']); - - $this->assertSame('123', $outboundAdapter->getSentMessages()[0]->getHeaders()->get('orderId')); - } - - public function test_flushing_unawaited_deliveries_awaits_only_unresolved_futures(): void - { - $outboundAdapter = new InMemoryAsyncOutboundAdapter(); - $ecotoneLite = $this->bootstrapEcotone($outboundAdapter); - $publisher = $ecotoneLite->getGateway(InMemoryAsyncPublisherModule::PUBLISHER_REFERENCE); - - $resolvedFuture = $publisher->asyncPublish('first order'); - $resolvedFuture->resolve(); - $publisher->asyncPublish('second order'); - - $ecotoneLite->getServiceFromContainer(AsyncPublishingRegistry::class)->flushUnawaitedDeliveries(); - - $this->assertSame(2, $outboundAdapter->awaitedDeliveriesCount()); - $this->assertSame(2, $outboundAdapter->totalAwaitCalls()); - } - - private function bootstrapPublisher(InMemoryAsyncOutboundAdapter $outboundAdapter): MessagePublisher - { - return $this->bootstrapEcotone($outboundAdapter)->getGateway(InMemoryAsyncPublisherModule::PUBLISHER_REFERENCE); - } - - private function bootstrapEcotone(InMemoryAsyncOutboundAdapter $outboundAdapter): FlowTestSupport - { - return EcotoneLite::bootstrapFlowTesting( - [InMemoryAsyncPublisherModule::class, InMemoryAsyncOutboundAdapter::class], - [$outboundAdapter], - ); - } -} diff --git a/packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/DeadLetterOfFailedBatchDeliveriesTest.php b/packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/DeadLetterOfFailedBatchDeliveriesTest.php similarity index 88% rename from packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/DeadLetterOfFailedBatchDeliveriesTest.php rename to packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/DeadLetterOfFailedBatchDeliveriesTest.php index 426a3bf33..92b4ee755 100644 --- a/packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/DeadLetterOfFailedBatchDeliveriesTest.php +++ b/packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/DeadLetterOfFailedBatchDeliveriesTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Unit\Channel\AsyncPublishing; +namespace Test\Ecotone\Messaging\Unit\Channel\DeliveryConfirmation; use Ecotone\Lite\EcotoneLite; use Ecotone\Messaging\BatchMessage; @@ -15,10 +15,10 @@ use Ecotone\Messaging\PollableChannel; use Ecotone\Messaging\Support\MessageBuilder; use PHPUnit\Framework\TestCase; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\AsyncOrderSubscriber; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\InMemoryAsyncPublishingChannelBuilder; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\OperationsLog; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\OrderService; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\AsyncOrderSubscriber; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\InMemoryHighThroughputPublishingChannelBuilder; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\OperationsLog; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\OrderService; /** * licence Apache-2.0 @@ -57,7 +57,7 @@ public function test_failed_async_batch_deliveries_are_stored_as_separate_dead_l ->withErrorChannel('dead_letters'), ]), enableAsynchronousProcessing: [ - InMemoryAsyncPublishingChannelBuilder::create('async_orders'), + InMemoryHighThroughputPublishingChannelBuilder::create('async_orders'), SimpleMessageChannelBuilder::createQueueChannel('dead_letters'), ], ); @@ -87,7 +87,7 @@ private function bootstrapWithDeadLetterChannel(): \Ecotone\Lite\Test\FlowTestSu ->withErrorChannel('dead_letters'), ]), enableAsynchronousProcessing: [ - InMemoryAsyncPublishingChannelBuilder::create('async_orders'), + InMemoryHighThroughputPublishingChannelBuilder::create('async_orders'), SimpleMessageChannelBuilder::createQueueChannel('dead_letters'), ], ); diff --git a/packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/AsyncPublishingChannelTest.php b/packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/HighThroughputPublishingChannelTest.php similarity index 78% rename from packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/AsyncPublishingChannelTest.php rename to packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/HighThroughputPublishingChannelTest.php index fc87f03da..1035afc01 100644 --- a/packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/AsyncPublishingChannelTest.php +++ b/packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/HighThroughputPublishingChannelTest.php @@ -2,24 +2,24 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Unit\Channel\AsyncPublishing; +namespace Test\Ecotone\Messaging\Unit\Channel\DeliveryConfirmation; use Ecotone\Lite\EcotoneLite; use Ecotone\Lite\Test\FlowTestSupport; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Channel\MessageChannelInterceptorAdapter; use PHPUnit\Framework\TestCase; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\AsyncOrderSubscriber; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\FakeTransactionModule; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\InMemoryAsyncPublishingChannelBuilder; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\OperationsLog; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\OrderService; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\AsyncOrderSubscriber; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\FakeTransactionModule; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\InMemoryHighThroughputPublishingChannelBuilder; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\OperationsLog; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\OrderService; /** * licence Apache-2.0 * @internal */ -final class AsyncPublishingChannelTest extends TestCase +final class HighThroughputPublishingChannelTest extends TestCase { public function test_messages_published_from_command_handler_are_awaited_before_transaction_commits(): void { @@ -75,7 +75,7 @@ private function bootstrapEcotone(OperationsLog $operationsLog): FlowTestSupport [OrderService::class, AsyncOrderSubscriber::class, FakeTransactionModule::class], [new OrderService($operationsLog), new AsyncOrderSubscriber(), OperationsLog::class => $operationsLog], enableAsynchronousProcessing: [ - InMemoryAsyncPublishingChannelBuilder::create('async_orders'), + InMemoryHighThroughputPublishingChannelBuilder::create('async_orders'), ], ); } diff --git a/packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/AsyncPublishingCollectorMatrixTest.php b/packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/HighThroughputPublishingCollectorMatrixTest.php similarity index 77% rename from packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/AsyncPublishingCollectorMatrixTest.php rename to packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/HighThroughputPublishingCollectorMatrixTest.php index 8841b2a1f..adeb46aae 100644 --- a/packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/AsyncPublishingCollectorMatrixTest.php +++ b/packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/HighThroughputPublishingCollectorMatrixTest.php @@ -2,25 +2,25 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Unit\Channel\AsyncPublishing; +namespace Test\Ecotone\Messaging\Unit\Channel\DeliveryConfirmation; use Ecotone\Lite\EcotoneLite; use Ecotone\Lite\Test\FlowTestSupport; use Ecotone\Messaging\Channel\PollableChannel\PollableChannelConfiguration; use Ecotone\Messaging\Config\ServiceConfiguration; use PHPUnit\Framework\TestCase; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\AsyncOrderSubscriber; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\FakeTransactionModule; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\InMemoryAsyncPublishingChannelBuilder; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\OperationsLog; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\OrderService; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\OrderWasPlaced; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\AsyncOrderSubscriber; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\FakeTransactionModule; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\InMemoryHighThroughputPublishingChannelBuilder; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\OperationsLog; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\OrderService; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\OrderWasPlaced; /** * licence Apache-2.0 * @internal */ -final class AsyncPublishingCollectorMatrixTest extends TestCase +final class HighThroughputPublishingCollectorMatrixTest extends TestCase { public function test_with_collector_disabled_messages_fire_directly_during_handler_and_await_before_commit(): void { @@ -70,7 +70,7 @@ private function bootstrapEcotone(OperationsLog $operationsLog, bool $collectorE PollableChannelConfiguration::neverRetry('async_orders')->withCollector($collectorEnabled), ]), enableAsynchronousProcessing: [ - InMemoryAsyncPublishingChannelBuilder::create('async_orders'), + InMemoryHighThroughputPublishingChannelBuilder::create('async_orders'), ], ); } diff --git a/packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/AsyncPublishingReliabilityTest.php b/packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/HighThroughputPublishingReliabilityTest.php similarity index 71% rename from packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/AsyncPublishingReliabilityTest.php rename to packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/HighThroughputPublishingReliabilityTest.php index dbabab250..5d4c3b3f9 100644 --- a/packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/AsyncPublishingReliabilityTest.php +++ b/packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/HighThroughputPublishingReliabilityTest.php @@ -2,14 +2,14 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Unit\Channel\AsyncPublishing; +namespace Test\Ecotone\Messaging\Unit\Channel\DeliveryConfirmation; use Ecotone\Lite\EcotoneLite; use Ecotone\Messaging\Attribute\Parameter\Reference; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; -use Ecotone\Messaging\Channel\AsyncPublishing\DeliveryFuture; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; +use Ecotone\Messaging\Channel\DeliveryConfirmation\DeliveryFuture; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDeliveryRegistry; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Channel\MessageChannelInterceptorAdapter; use Ecotone\Messaging\Config\ServiceConfiguration; use Ecotone\Messaging\Handler\Logger\LoggingService; @@ -17,20 +17,20 @@ use Ecotone\Messaging\Support\MessageBuilder; use Ecotone\Modelling\Attribute\CommandHandler; use PHPUnit\Framework\TestCase; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\AsyncOrderSubscriber; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\FakeTransactionModule; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\InMemoryAsyncOutboundAdapter; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\InMemoryAsyncPublisherModule; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\InMemoryAsyncPublishingChannelBuilder; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\InMemoryPendingDelivery; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\OperationsLog; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\OrderService; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\AsyncOrderSubscriber; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\FakeTransactionModule; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\InMemoryHighThroughputOutboundAdapter; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\InMemoryHighThroughputPublisherModule; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\InMemoryHighThroughputPublishingChannelBuilder; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\InMemoryPendingDelivery; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\OperationsLog; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\OrderService; /** * licence Apache-2.0 * @internal */ -final class AsyncPublishingReliabilityTest extends TestCase +final class HighThroughputPublishingReliabilityTest extends TestCase { public function test_future_throwing_during_await_keeps_reporting_failure_on_subsequent_resolves(): void { @@ -73,8 +73,8 @@ public function test_failed_deliveries_routed_to_async_error_channel_are_awaited \Ecotone\Messaging\Channel\PollableChannel\GlobalPollableChannelConfiguration::createWithDefaults()->withErrorChannel('failure_channel'), ]), enableAsynchronousProcessing: [ - InMemoryAsyncPublishingChannelBuilder::create('async_orders'), - InMemoryAsyncPublishingChannelBuilder::create('failure_channel'), + InMemoryHighThroughputPublishingChannelBuilder::create('async_orders'), + InMemoryHighThroughputPublishingChannelBuilder::create('failure_channel'), ], ); $ordersChannel = $ecotoneLite->getMessageChannel('async_orders'); @@ -91,17 +91,17 @@ public function test_failed_deliveries_routed_to_async_error_channel_are_awaited public function test_future_of_delivery_flushed_by_backlog_limit_still_reports_failure(): void { - $outboundAdapter = new InMemoryAsyncOutboundAdapter(); + $outboundAdapter = new InMemoryHighThroughputOutboundAdapter(); $ecotoneLite = EcotoneLite::bootstrapFlowTesting( - [InMemoryAsyncPublisherModule::class, InMemoryAsyncOutboundAdapter::class], + [InMemoryHighThroughputPublisherModule::class, InMemoryHighThroughputOutboundAdapter::class], [$outboundAdapter], ); - $publisher = $ecotoneLite->getGateway(InMemoryAsyncPublisherModule::PUBLISHER_REFERENCE); + $publisher = $ecotoneLite->getGateway(InMemoryHighThroughputPublisherModule::PUBLISHER_REFERENCE); $outboundAdapter->failDeliveriesWith('broker down'); - $firstFuture = $publisher->asyncPublish('first order'); + $firstFuture = $publisher->publishDeferred('first order'); for ($messageNumber = 0; $messageNumber < 1300; $messageNumber++) { - $publisher->asyncPublish('unresolved order ' . $messageNumber); + $publisher->publishDeferred('unresolved order ' . $messageNumber); } $this->assertGreaterThan(0, $outboundAdapter->awaitedDeliveriesCount()); @@ -118,15 +118,15 @@ public function test_unawaited_deliveries_of_all_registries_are_flushed_on_scrip require $argv[1]; - $unawaitedDelivery = new class implements \Ecotone\Messaging\Channel\AsyncPublishing\PendingDelivery { + $unawaitedDelivery = new class implements \Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDelivery { private bool $awaited = false; - public function awaitDelivery(): \Ecotone\Messaging\Channel\AsyncPublishing\DeliveryResult + public function awaitDelivery(): \Ecotone\Messaging\Channel\DeliveryConfirmation\DeliveryResult { $this->awaited = true; echo 'flushed;'; - return \Ecotone\Messaging\Channel\AsyncPublishing\DeliveryResult::successful(); + return \Ecotone\Messaging\Channel\DeliveryConfirmation\DeliveryResult::successful(); } public function isAwaited(): bool @@ -135,9 +135,9 @@ public function isAwaited(): bool } }; - $firstRegistry = new \Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry(new \Ecotone\Messaging\Handler\Logger\LoggingService()); + $firstRegistry = new \Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDeliveryRegistry(new \Ecotone\Messaging\Handler\Logger\LoggingService()); $firstRegistry->register('orders', $unawaitedDelivery); - $secondRegistry = new \Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry(new \Ecotone\Messaging\Handler\Logger\LoggingService()); + $secondRegistry = new \Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDeliveryRegistry(new \Ecotone\Messaging\Handler\Logger\LoggingService()); $secondRegistry->register('shipments', clone $unawaitedDelivery); echo 'script finished;'; @@ -166,7 +166,7 @@ private function nearestComposerAutoloadPath(): string public function test_shutdown_flush_continues_when_one_delivery_throws(): void { - $registry = new AsyncPublishingRegistry(new LoggingService()); + $registry = new PendingDeliveryRegistry(new LoggingService()); $throwingDelivery = new InMemoryPendingDelivery(MessageBuilder::withPayload('first order')->build(), throwOnAwait: true); $followingDelivery = new InMemoryPendingDelivery(MessageBuilder::withPayload('second order')->build()); $registry->register('orders', $throwingDelivery); @@ -179,7 +179,7 @@ public function test_shutdown_flush_continues_when_one_delivery_throws(): void public function test_closing_scope_awaits_deliveries_left_unawaited_when_execution_fails_before_await(): void { - $registry = new AsyncPublishingRegistry(new LoggingService()); + $registry = new PendingDeliveryRegistry(new LoggingService()); $registry->openScope(); $delivery = new InMemoryPendingDelivery(MessageBuilder::withPayload('order')->build()); $registry->register('orders', $delivery); @@ -206,18 +206,18 @@ public function test_future_awaits_remaining_deliveries_when_earlier_delivery_th public function test_one_failing_batch_among_many_published_in_command_handler_rolls_back_transaction(): void { $operationsLog = new OperationsLog(); - $outboundAdapter = new InMemoryAsyncOutboundAdapter(); + $outboundAdapter = new InMemoryHighThroughputOutboundAdapter(); $commandHandler = new class () { #[CommandHandler('order.placeAllBatches')] - public function handle(string $order, #[Reference(InMemoryAsyncPublisherModule::PUBLISHER_REFERENCE)] MessagePublisher $publisher): void + public function handle(string $order, #[Reference(InMemoryHighThroughputPublisherModule::PUBLISHER_REFERENCE)] MessagePublisher $publisher): void { - $publisher->asyncPublish(BatchMessage::constructEmpty()->append($order . ' first batch')); - $publisher->asyncPublish(BatchMessage::constructEmpty()->append($order . ' poisoned batch')); - $publisher->asyncPublish(BatchMessage::constructEmpty()->append($order . ' third batch')); + $publisher->publishDeferred(BatchMessage::constructEmpty()->append($order . ' first batch')); + $publisher->publishDeferred(BatchMessage::constructEmpty()->append($order . ' poisoned batch')); + $publisher->publishDeferred(BatchMessage::constructEmpty()->append($order . ' third batch')); } }; $ecotoneLite = EcotoneLite::bootstrapFlowTesting( - [$commandHandler::class, InMemoryAsyncPublisherModule::class, InMemoryAsyncOutboundAdapter::class, FakeTransactionModule::class], + [$commandHandler::class, InMemoryHighThroughputPublisherModule::class, InMemoryHighThroughputOutboundAdapter::class, FakeTransactionModule::class], [$commandHandler, $outboundAdapter, OperationsLog::class => $operationsLog], ); $outboundAdapter->failDeliveriesContaining('poisoned', 'broker rejected the batch'); @@ -237,15 +237,15 @@ public function handle(string $order, #[Reference(InMemoryAsyncPublisherModule:: public function test_unresolved_publisher_futures_above_backlog_limit_are_flushed(): void { - $outboundAdapter = new InMemoryAsyncOutboundAdapter(); + $outboundAdapter = new InMemoryHighThroughputOutboundAdapter(); $ecotoneLite = EcotoneLite::bootstrapFlowTesting( - [InMemoryAsyncPublisherModule::class, InMemoryAsyncOutboundAdapter::class], + [InMemoryHighThroughputPublisherModule::class, InMemoryHighThroughputOutboundAdapter::class], [$outboundAdapter], ); - $publisher = $ecotoneLite->getGateway(InMemoryAsyncPublisherModule::PUBLISHER_REFERENCE); + $publisher = $ecotoneLite->getGateway(InMemoryHighThroughputPublisherModule::PUBLISHER_REFERENCE); for ($messageNumber = 0; $messageNumber < 1300; $messageNumber++) { - $publisher->asyncPublish('unresolved order ' . $messageNumber); + $publisher->publishDeferred('unresolved order ' . $messageNumber); } $this->assertGreaterThan(0, $outboundAdapter->awaitedDeliveriesCount()); diff --git a/packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/AsyncPublishingScenariosTest.php b/packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/HighThroughputPublishingScenariosTest.php similarity index 87% rename from packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/AsyncPublishingScenariosTest.php rename to packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/HighThroughputPublishingScenariosTest.php index e86d34619..d6a9c667f 100644 --- a/packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/AsyncPublishingScenariosTest.php +++ b/packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/HighThroughputPublishingScenariosTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Unit\Channel\AsyncPublishing; +namespace Test\Ecotone\Messaging\Unit\Channel\DeliveryConfirmation; use Ecotone\Lite\EcotoneLite; use Ecotone\Lite\Test\FlowTestSupport; @@ -15,20 +15,20 @@ use Ecotone\Messaging\Handler\Recoverability\ErrorContext; use Ecotone\Messaging\MessageHeaders; use PHPUnit\Framework\TestCase; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\AsyncOrderForwarder; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\AsyncOrderSubscriber; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\FakeTransactionModule; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\InMemoryAsyncPublishingChannelBuilder; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\OperationsLog; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\OrderRequestReceived; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\OrderService; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\OrderWasPlaced; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\AsyncOrderForwarder; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\AsyncOrderSubscriber; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\FakeTransactionModule; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\InMemoryHighThroughputPublishingChannelBuilder; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\OperationsLog; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\OrderRequestReceived; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\OrderService; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\OrderWasPlaced; /** * licence Apache-2.0 * @internal */ -final class AsyncPublishingScenariosTest extends TestCase +final class HighThroughputPublishingScenariosTest extends TestCase { public function test_nested_command_bus_awaits_deliveries_once_at_outermost_boundary(): void { @@ -107,7 +107,7 @@ public function test_failed_deliveries_are_routed_to_error_channel_and_transacti PollableChannelConfiguration::neverRetry('async_orders')->withCollector(false)->withErrorChannel('failure_channel'), ]), enableAsynchronousProcessing: [ - InMemoryAsyncPublishingChannelBuilder::create('async_orders'), + InMemoryHighThroughputPublishingChannelBuilder::create('async_orders'), SimpleMessageChannelBuilder::createQueueChannel('failure_channel'), ], ); @@ -139,7 +139,7 @@ public function test_only_failed_message_from_batch_is_routed_to_error_channel() PollableChannelConfiguration::neverRetry('async_orders')->withCollector(false)->withErrorChannel('failure_channel'), ]), enableAsynchronousProcessing: [ - InMemoryAsyncPublishingChannelBuilder::create('async_orders'), + InMemoryHighThroughputPublishingChannelBuilder::create('async_orders'), SimpleMessageChannelBuilder::createQueueChannel('failure_channel'), ], ); @@ -168,7 +168,7 @@ private function bootstrapEcotone(OperationsLog $operationsLog): FlowTestSupport OperationsLog::class => $operationsLog, ], enableAsynchronousProcessing: [ - InMemoryAsyncPublishingChannelBuilder::create('async_orders'), + InMemoryHighThroughputPublishingChannelBuilder::create('async_orders'), SimpleMessageChannelBuilder::createQueueChannel('incoming_orders'), ], ); diff --git a/packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/MessagePublisherAsyncPublishBatchTest.php b/packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/MessagePublisherPublishDeferredBatchTest.php similarity index 63% rename from packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/MessagePublisherAsyncPublishBatchTest.php rename to packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/MessagePublisherPublishDeferredBatchTest.php index 6a815d99c..5ee02c7d3 100644 --- a/packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/MessagePublisherAsyncPublishBatchTest.php +++ b/packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/MessagePublisherPublishDeferredBatchTest.php @@ -2,27 +2,27 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Unit\Channel\AsyncPublishing; +namespace Test\Ecotone\Messaging\Unit\Channel\DeliveryConfirmation; use Ecotone\Lite\EcotoneLite; use Ecotone\Messaging\BatchMessage; use Ecotone\Messaging\MessagePublisher; use PHPUnit\Framework\TestCase; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\InMemoryAsyncOutboundAdapter; -use Test\Ecotone\Messaging\Fixture\AsyncPublishing\InMemoryAsyncPublisherModule; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\InMemoryHighThroughputOutboundAdapter; +use Test\Ecotone\Messaging\Fixture\HighThroughputPublishing\InMemoryHighThroughputPublisherModule; /** * licence Apache-2.0 * @internal */ -final class MessagePublisherAsyncPublishBatchTest extends TestCase +final class MessagePublisherPublishDeferredBatchTest extends TestCase { public function test_publishing_batch_delivers_whole_batch_with_single_pending_delivery(): void { - $outboundAdapter = new InMemoryAsyncOutboundAdapter(); + $outboundAdapter = new InMemoryHighThroughputOutboundAdapter(); $publisher = $this->bootstrapPublisher($outboundAdapter); - $future = $publisher->asyncPublish( + $future = $publisher->publishDeferred( BatchMessage::constructEmpty() ->append('first order') ->append('second order', ['priority' => 5]) @@ -47,22 +47,22 @@ public function test_publishing_batch_delivers_whole_batch_with_single_pending_d public function test_publishing_empty_batch_resolves_without_sending_anything(): void { - $outboundAdapter = new InMemoryAsyncOutboundAdapter(); + $outboundAdapter = new InMemoryHighThroughputOutboundAdapter(); $publisher = $this->bootstrapPublisher($outboundAdapter); - $future = $publisher->asyncPublish(BatchMessage::constructEmpty()); + $future = $publisher->publishDeferred(BatchMessage::constructEmpty()); $future->resolve(); $this->assertCount(0, $outboundAdapter->getSentMessages()); } - private function bootstrapPublisher(InMemoryAsyncOutboundAdapter $outboundAdapter): MessagePublisher + private function bootstrapPublisher(InMemoryHighThroughputOutboundAdapter $outboundAdapter): MessagePublisher { $ecotoneLite = EcotoneLite::bootstrapFlowTesting( - [InMemoryAsyncPublisherModule::class, InMemoryAsyncOutboundAdapter::class], + [InMemoryHighThroughputPublisherModule::class, InMemoryHighThroughputOutboundAdapter::class], [$outboundAdapter], ); - return $ecotoneLite->getGateway(InMemoryAsyncPublisherModule::PUBLISHER_REFERENCE); + return $ecotoneLite->getGateway(InMemoryHighThroughputPublisherModule::PUBLISHER_REFERENCE); } } diff --git a/packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/MessagePublisherPublishDeferredTest.php b/packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/MessagePublisherPublishDeferredTest.php new file mode 100644 index 000000000..717d2cea8 --- /dev/null +++ b/packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/MessagePublisherPublishDeferredTest.php @@ -0,0 +1,112 @@ +bootstrapPublisher($outboundAdapter); + + $future = $publisher->publishDeferred('order was placed'); + + $this->assertSame(['order was placed'], $outboundAdapter->getSentPayloads()); + $this->assertSame(0, $outboundAdapter->awaitedDeliveriesCount()); + + $future->resolve(); + + $this->assertSame(1, $outboundAdapter->awaitedDeliveriesCount()); + } + + public function test_resolving_future_twice_awaits_delivery_only_once(): void + { + $outboundAdapter = new InMemoryHighThroughputOutboundAdapter(); + $publisher = $this->bootstrapPublisher($outboundAdapter); + + $future = $publisher->publishDeferred('order was placed'); + $future->resolve(); + $future->resolve(); + + $this->assertSame(1, $outboundAdapter->totalAwaitCalls()); + } + + public function test_resolving_future_throws_when_delivery_failed(): void + { + $outboundAdapter = new InMemoryHighThroughputOutboundAdapter(); + $outboundAdapter->failDeliveriesWith('broker rejected message'); + $publisher = $this->bootstrapPublisher($outboundAdapter); + + $future = $publisher->publishDeferred('order was placed'); + + $this->expectException(PublishingFailedException::class); + + $future->resolve(); + } + + public function test_publish_deferred_on_publisher_without_non_blocking_confirmation_throws_clear_exception(): void + { + $outboundAdapter = new InMemoryHighThroughputOutboundAdapter(); + $outboundAdapter->actAsSynchronousPublisher(); + $publisher = $this->bootstrapPublisher($outboundAdapter); + + $this->expectException(PublishingFailedException::class); + $this->expectExceptionMessageMatches('/not configured for non blocking confirmation/'); + + $publisher->publishDeferred('order was placed'); + } + + public function test_metadata_passed_to_publish_deferred_lands_on_published_message(): void + { + $outboundAdapter = new InMemoryHighThroughputOutboundAdapter(); + $publisher = $this->bootstrapPublisher($outboundAdapter); + + $publisher->publishDeferred('order was placed', metadata: ['orderId' => '123']); + + $this->assertSame('123', $outboundAdapter->getSentMessages()[0]->getHeaders()->get('orderId')); + } + + public function test_flushing_unawaited_deliveries_awaits_only_unresolved_futures(): void + { + $outboundAdapter = new InMemoryHighThroughputOutboundAdapter(); + $ecotoneLite = $this->bootstrapEcotone($outboundAdapter); + $publisher = $ecotoneLite->getGateway(InMemoryHighThroughputPublisherModule::PUBLISHER_REFERENCE); + + $resolvedFuture = $publisher->publishDeferred('first order'); + $resolvedFuture->resolve(); + $publisher->publishDeferred('second order'); + + $ecotoneLite->getServiceFromContainer(PendingDeliveryRegistry::class)->flushUnawaitedDeliveries(); + + $this->assertSame(2, $outboundAdapter->awaitedDeliveriesCount()); + $this->assertSame(2, $outboundAdapter->totalAwaitCalls()); + } + + private function bootstrapPublisher(InMemoryHighThroughputOutboundAdapter $outboundAdapter): MessagePublisher + { + return $this->bootstrapEcotone($outboundAdapter)->getGateway(InMemoryHighThroughputPublisherModule::PUBLISHER_REFERENCE); + } + + private function bootstrapEcotone(InMemoryHighThroughputOutboundAdapter $outboundAdapter): FlowTestSupport + { + return EcotoneLite::bootstrapFlowTesting( + [InMemoryHighThroughputPublisherModule::class, InMemoryHighThroughputOutboundAdapter::class], + [$outboundAdapter], + ); + } +} diff --git a/packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/SendRetryOfFailedBatchDeliveriesTest.php b/packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/SendRetryOfFailedBatchDeliveriesTest.php similarity index 94% rename from packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/SendRetryOfFailedBatchDeliveriesTest.php rename to packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/SendRetryOfFailedBatchDeliveriesTest.php index 79040a1eb..4b1e0198c 100644 --- a/packages/Ecotone/tests/Messaging/Unit/Channel/AsyncPublishing/SendRetryOfFailedBatchDeliveriesTest.php +++ b/packages/Ecotone/tests/Messaging/Unit/Channel/DeliveryConfirmation/SendRetryOfFailedBatchDeliveriesTest.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace Test\Ecotone\Messaging\Unit\Channel\AsyncPublishing; +namespace Test\Ecotone\Messaging\Unit\Channel\DeliveryConfirmation; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\FailedDelivery; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; +use Ecotone\Messaging\Channel\DeliveryConfirmation\FailedDelivery; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Channel\PollableChannel\SendRetries\SendRetryChannelInterceptor; use Ecotone\Messaging\Channel\PollableChannel\Serialization\OutboundMessageConverter; use Ecotone\Messaging\Config\ConfiguredMessagingSystem; diff --git a/packages/Ecotone/tests/Messaging/Unit/PrecedenceTest.php b/packages/Ecotone/tests/Messaging/Unit/PrecedenceTest.php index 87ebd5e9a..97a1a5faf 100644 --- a/packages/Ecotone/tests/Messaging/Unit/PrecedenceTest.php +++ b/packages/Ecotone/tests/Messaging/Unit/PrecedenceTest.php @@ -15,8 +15,8 @@ final class PrecedenceTest extends TestCase { public function test_async_publishing_await_runs_inside_transaction_and_outside_collector_release(): void { - $this->assertGreaterThan(Precedence::DATABASE_TRANSACTION_PRECEDENCE, Precedence::ASYNC_PUBLISHING_AWAIT_PRECEDENCE); - $this->assertGreaterThan(Precedence::ASYNC_PUBLISHING_AWAIT_PRECEDENCE, Precedence::COLLECTOR_SENDER_PRECEDENCE); + $this->assertGreaterThan(Precedence::DATABASE_TRANSACTION_PRECEDENCE, Precedence::DELIVERY_CONFIRMATION_PRECEDENCE); + $this->assertGreaterThan(Precedence::DELIVERY_CONFIRMATION_PRECEDENCE, Precedence::COLLECTOR_SENDER_PRECEDENCE); $this->assertGreaterThan(Precedence::COLLECTOR_SENDER_PRECEDENCE, Precedence::DATABASE_OBJECT_MANAGER_PRECEDENCE); $this->assertGreaterThan(Precedence::DATABASE_OBJECT_MANAGER_PRECEDENCE, Precedence::LAZY_EVENT_PUBLICATION_PRECEDENCE); } diff --git a/packages/Enqueue/src/EnqueueOutboundChannelAdapter.php b/packages/Enqueue/src/EnqueueOutboundChannelAdapter.php index addbe7121..6479074c9 100644 --- a/packages/Enqueue/src/EnqueueOutboundChannelAdapter.php +++ b/packages/Enqueue/src/EnqueueOutboundChannelAdapter.php @@ -5,8 +5,6 @@ namespace Ecotone\Enqueue; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; -use Ecotone\Messaging\Channel\AsyncPublishing\ConfirmedDelivery; use Ecotone\Messaging\Channel\PollableChannel\Serialization\OutboundMessage; use Ecotone\Messaging\Channel\PollableChannel\Serialization\OutboundMessageConverter; use Ecotone\Messaging\Config\ConfigurationException; @@ -33,9 +31,8 @@ public function __construct( protected bool $autoDeclare, protected OutboundMessageConverter $outboundMessageConverter, private ConversionService $conversionService, - private AsyncPublishingRegistry $asyncPublishingRegistry, - private bool $asyncPublishing = false, - private string $asyncPublishingChannelName = '', + private bool $batchPublishing = false, + private string $channelName = '', ) { } @@ -43,8 +40,8 @@ abstract public function initialize(): void; public function handle(Message $message): void { - if ($message->getPayload() instanceof BatchMessage && ! $this->asyncPublishing) { - throw ConfigurationException::create(sprintf('Sending BatchMessage over `%s` requires async publishing to be enabled. Enable it with withAsyncPublishing(), available as part of Ecotone Enterprise.', $this->asyncPublishingChannelName)); + if ($message->getPayload() instanceof BatchMessage && ! $this->batchPublishing) { + throw ConfigurationException::create(sprintf('Sending BatchMessage over `%s` requires batch publishing to be enabled. Enable it with withHighThroughputPublishing(), available as part of Ecotone Enterprise.', $this->channelName)); } $context = $this->createOutboundContext(); @@ -54,8 +51,6 @@ public function handle(Message $message): void } else { $this->sendSingleMessage($message, $context); } - - $this->registerSynchronouslyConfirmedDelivery(); } protected function createOutboundContext(): Context @@ -73,15 +68,6 @@ protected function createOutboundContext(): Context return $context; } - protected function registerSynchronouslyConfirmedDelivery(): void - { - if (! $this->asyncPublishing || ! $this->asyncPublishingRegistry->isScopeActive()) { - return; - } - - $this->asyncPublishingRegistry->register($this->asyncPublishingChannelName, new ConfirmedDelivery()); - } - protected function handleBatch(BatchMessage $batchMessage, Context $context): void { foreach ($batchMessage->getEntries() as $entry) { diff --git a/packages/Kafka/src/Channel/KafkaMessageChannel.php b/packages/Kafka/src/Channel/KafkaMessageChannel.php index 99a5bca78..45ca35c8a 100644 --- a/packages/Kafka/src/Channel/KafkaMessageChannel.php +++ b/packages/Kafka/src/Channel/KafkaMessageChannel.php @@ -26,7 +26,7 @@ public function __construct( public function supportsBatchMessages(): bool { - return $this->outboundChannelAdapter->isAsyncPublishingEnabled(); + return $this->outboundChannelAdapter->isBatchPublishingEnabled(); } public function send(Message $message): void diff --git a/packages/Kafka/src/Channel/KafkaMessageChannelBuilder.php b/packages/Kafka/src/Channel/KafkaMessageChannelBuilder.php index fcabf14ba..807f29d1b 100644 --- a/packages/Kafka/src/Channel/KafkaMessageChannelBuilder.php +++ b/packages/Kafka/src/Channel/KafkaMessageChannelBuilder.php @@ -26,8 +26,9 @@ final class KafkaMessageChannelBuilder implements MessageChannelWithSerializatio private KafkaOutboundChannelAdapterBuilder $outboundChannelAdapterBuilder; private string $headerMapper; private ?MediaType $conversionMediaType = null; - private bool $asyncPublishing = false; - private ?int $asyncPublishingTimeout = null; + private bool $batchPublishing = false; + private bool $nonBlockingConfirmation = false; + private ?int $confirmationTimeout = null; private function __construct( private string $channelName, @@ -119,25 +120,36 @@ public function withDefaultConversionMediaType(string $mediaType): self return $this; } - public function withHighThroughputPublishing(bool $enabled = true, ?int $timeoutInMilliseconds = null): self + /** + * @param bool $batchPublishing coalesces produced Messages into broker side batches by enabling producer lingering + * @param bool $nonBlockingConfirmation produces without flushing, delivery reports are awaited before the surrounding Command Bus or asynchronous endpoint finishes + * @param int|null $confirmationTimeoutInMilliseconds how long to await delivery reports before treating the delivery as failed + */ + public function withHighThroughputPublishing(bool $batchPublishing = true, bool $nonBlockingConfirmation = true, ?int $confirmationTimeoutInMilliseconds = null): self { - Assert::isTrue($timeoutInMilliseconds === null || $timeoutInMilliseconds > 0, 'Async publishing timeout must be a positive amount of milliseconds.'); - $this->asyncPublishing = $enabled; - if ($timeoutInMilliseconds !== null) { - $this->asyncPublishingTimeout = $timeoutInMilliseconds; + Assert::isTrue($confirmationTimeoutInMilliseconds === null || $confirmationTimeoutInMilliseconds > 0, 'Confirmation timeout must be a positive amount of milliseconds.'); + $this->batchPublishing = $batchPublishing; + $this->nonBlockingConfirmation = $nonBlockingConfirmation; + if ($confirmationTimeoutInMilliseconds !== null) { + $this->confirmationTimeout = $confirmationTimeoutInMilliseconds; } return $this; } - public function isHighThroughputPublishingEnabled(): bool + public function isBatchPublishingEnabled(): bool + { + return $this->batchPublishing; + } + + public function isNonBlockingConfirmationEnabled(): bool { - return $this->asyncPublishing; + return $this->nonBlockingConfirmation; } - public function getAsyncPublishingTimeout(): ?int + public function getConfirmationTimeout(): ?int { - return $this->asyncPublishingTimeout; + return $this->confirmationTimeout; } /** diff --git a/packages/Kafka/src/Configuration/KafkaModule.php b/packages/Kafka/src/Configuration/KafkaModule.php index ad1883bfe..4cc9b884c 100644 --- a/packages/Kafka/src/Configuration/KafkaModule.php +++ b/packages/Kafka/src/Configuration/KafkaModule.php @@ -11,7 +11,7 @@ use Ecotone\Kafka\Inbound\KafkaInboundChannelAdapterBuilder; use Ecotone\Kafka\Outbound\KafkaOutboundChannelAdapterBuilder; use Ecotone\Messaging\Attribute\ModuleAnnotation; -use Ecotone\Messaging\Channel\AsyncPublishing\Config\AsyncPublishGatewayRegistration; +use Ecotone\Messaging\Channel\DeliveryConfirmation\Config\DeferredPublishingGatewayRegistration; use Ecotone\Messaging\Config\Annotation\AnnotationModule; use Ecotone\Messaging\Config\Annotation\ModuleConfiguration\ExtensionObjectResolver; use Ecotone\Messaging\Config\Annotation\ModuleConfiguration\NoExternalConfigurationModule; @@ -113,7 +113,7 @@ public function prepare(Configuration $messagingConfiguration, array $extensionO MessagePublisher::class . '::' . $extensionObject->getMessageChannelName(), ) ->withHeaderMapper($extensionObject->getHeaderMapper()) - ->withAsyncPublishing($extensionObject->isHighThroughputPublishingEnabled(), $extensionObject->getAsyncPublishingTimeout()); + ->withHighThroughputPublishing($extensionObject->isBatchPublishingEnabled(), $extensionObject->isNonBlockingConfirmationEnabled(), $extensionObject->getConfirmationTimeout()); } } @@ -227,7 +227,7 @@ private function registerMessagePublisher(Configuration $messagingConfiguration, ->withDefaultConversionMediaType($mediaType) ); - AsyncPublishGatewayRegistration::registerFor($messagingConfiguration, $extensionObject->getReferenceName(), $extensionObject->isAsyncPublishingEnabled()); + DeferredPublishingGatewayRegistration::registerFor($messagingConfiguration, $extensionObject->getReferenceName(), $extensionObject->isNonBlockingConfirmationEnabled()); } private function getPublisherEndpointId(string $referenceName): string diff --git a/packages/Kafka/src/Configuration/KafkaPublisherConfiguration.php b/packages/Kafka/src/Configuration/KafkaPublisherConfiguration.php index c14098d6e..d5c621c92 100644 --- a/packages/Kafka/src/Configuration/KafkaPublisherConfiguration.php +++ b/packages/Kafka/src/Configuration/KafkaPublisherConfiguration.php @@ -21,7 +21,7 @@ final class KafkaPublisherConfiguration implements DefinedObject { public const ACKNOWLEDGE_TIMEOUT = '8000'; - public const DEFAULT_ASYNC_PUBLISHING_TIMEOUT = 12000; + public const DEFAULT_CONFIRMATION_TIMEOUT = 12000; /** * @param array $configuration @@ -33,8 +33,9 @@ public function __construct( private string $brokerConfigurationReference, private HeaderMapper $headerMapper, private ?string $outputDefaultConversionMediaType = null, - private bool $asyncPublishing = false, - private int $asyncPublishingTimeout = self::DEFAULT_ASYNC_PUBLISHING_TIMEOUT, + private bool $batchPublishing = false, + private bool $nonBlockingConfirmation = false, + private int $confirmationTimeout = self::DEFAULT_CONFIRMATION_TIMEOUT, ) { } @@ -106,25 +107,36 @@ public function getHeaderMapper(): HeaderMapper return $this->headerMapper; } - public function withAsyncPublishing(bool $asyncPublishing = true, ?int $timeoutInMilliseconds = null): self + /** + * @param bool $batchPublishing coalesces produced Messages into broker side batches by enabling producer lingering + * @param bool $nonBlockingConfirmation produces without flushing, delivery reports are awaited on Future::resolve() or before the surrounding Command Bus or asynchronous endpoint finishes + * @param int|null $confirmationTimeoutInMilliseconds how long to await delivery reports before treating the delivery as failed + */ + public function withHighThroughputPublishing(bool $batchPublishing = true, bool $nonBlockingConfirmation = true, ?int $confirmationTimeoutInMilliseconds = null): self { - Assert::isTrue($timeoutInMilliseconds === null || $timeoutInMilliseconds > 0, 'Async publishing timeout must be a positive amount of milliseconds.'); - $this->asyncPublishing = $asyncPublishing; - if ($timeoutInMilliseconds !== null) { - $this->asyncPublishingTimeout = $timeoutInMilliseconds; + Assert::isTrue($confirmationTimeoutInMilliseconds === null || $confirmationTimeoutInMilliseconds > 0, 'Confirmation timeout must be a positive amount of milliseconds.'); + $this->batchPublishing = $batchPublishing; + $this->nonBlockingConfirmation = $nonBlockingConfirmation; + if ($confirmationTimeoutInMilliseconds !== null) { + $this->confirmationTimeout = $confirmationTimeoutInMilliseconds; } return $this; } - public function isAsyncPublishingEnabled(): bool + public function isBatchPublishingEnabled(): bool + { + return $this->batchPublishing; + } + + public function isNonBlockingConfirmationEnabled(): bool { - return $this->asyncPublishing; + return $this->nonBlockingConfirmation; } - public function getAsyncPublishingTimeout(): int + public function getConfirmationTimeout(): int { - return $this->asyncPublishingTimeout; + return $this->confirmationTimeout; } public function getOutputDefaultConversionMediaType(): ?string @@ -140,7 +152,7 @@ public function getBrokerConfigurationReference(): string public function getAsKafkaConfig(): Conf { $configuration = $this->configuration; - if ($this->asyncPublishing && ! isset($configuration['linger.ms']) && ! isset($configuration['queue.buffering.max.ms'])) { + if ($this->batchPublishing && ! isset($configuration['linger.ms']) && ! isset($configuration['queue.buffering.max.ms'])) { $configuration['linger.ms'] = '20'; } @@ -171,8 +183,9 @@ public function getDefinition(): Definition $this->brokerConfigurationReference, $this->headerMapper->getDefinition(), $this->outputDefaultConversionMediaType, - $this->asyncPublishing, - $this->asyncPublishingTimeout, + $this->batchPublishing, + $this->nonBlockingConfirmation, + $this->confirmationTimeout, ]); } } diff --git a/packages/Kafka/src/Outbound/KafkaDeliveryTracker.php b/packages/Kafka/src/Outbound/KafkaDeliveryTracker.php index a6fc25814..1471f0ae9 100644 --- a/packages/Kafka/src/Outbound/KafkaDeliveryTracker.php +++ b/packages/Kafka/src/Outbound/KafkaDeliveryTracker.php @@ -4,8 +4,8 @@ namespace Ecotone\Kafka\Outbound; -use Ecotone\Messaging\Channel\AsyncPublishing\DeliveryResult; -use Ecotone\Messaging\Channel\AsyncPublishing\FailedDelivery; +use Ecotone\Messaging\Channel\DeliveryConfirmation\DeliveryResult; +use Ecotone\Messaging\Channel\DeliveryConfirmation\FailedDelivery; use Ecotone\Messaging\Message; use RdKafka\Message as KafkaMessage; diff --git a/packages/Kafka/src/Outbound/KafkaOutboundChannelAdapter.php b/packages/Kafka/src/Outbound/KafkaOutboundChannelAdapter.php index 1dedee701..f2ade7890 100644 --- a/packages/Kafka/src/Outbound/KafkaOutboundChannelAdapter.php +++ b/packages/Kafka/src/Outbound/KafkaOutboundChannelAdapter.php @@ -8,9 +8,9 @@ use Ecotone\Kafka\Configuration\KafkaAdmin; use Ecotone\Kafka\Configuration\KafkaPublisherConfiguration; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; -use Ecotone\Messaging\Channel\AsyncPublishing\FailedDelivery; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; +use Ecotone\Messaging\Channel\DeliveryConfirmation\FailedDelivery; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDeliveryRegistry; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Channel\PollableChannel\Serialization\OutboundMessageConverter; use Ecotone\Messaging\Config\ConfigurationException; use Ecotone\Messaging\Conversion\ConversionService; @@ -36,7 +36,7 @@ public function __construct( private KafkaAdmin $kafkaAdmin, private ConversionService $conversionService, private OutboundMessageConverter $outboundMessageConverter, - private AsyncPublishingRegistry $asyncPublishingRegistry, + private PendingDeliveryRegistry $pendingDeliveryRegistry, ) { } @@ -45,8 +45,8 @@ public function __construct( */ public function handle(Message $message): void { - if ($message->getPayload() instanceof BatchMessage && ! $this->isAsyncPublishingEnabled()) { - throw ConfigurationException::create(sprintf('Sending BatchMessage over `%s` requires async publishing to be enabled. Enable it with withAsyncPublishing(), available as part of Ecotone Enterprise.', $this->referenceName)); + if ($message->getPayload() instanceof BatchMessage && ! $this->isBatchPublishingEnabled()) { + throw ConfigurationException::create(sprintf('Sending BatchMessage over `%s` requires batch publishing to be enabled. Enable it with withHighThroughputPublishing(), available as part of Ecotone Enterprise.', $this->referenceName)); } $producer = $this->kafkaAdmin->getProducer($this->referenceName); @@ -61,7 +61,7 @@ public function handle(Message $message): void $deliveryId = $this->produce($message, $topic, trackDelivery: true); $producer->poll(0); - if ($this->canPublishAsynchronously()) { + if ($this->canDeferConfirmation()) { $this->registerPendingDelivery($producer, [$deliveryId]); return; @@ -70,9 +70,14 @@ public function handle(Message $message): void $this->flushSynchronously($producer, [$deliveryId]); } - public function isAsyncPublishingEnabled(): bool + public function isBatchPublishingEnabled(): bool { - return $this->kafkaAdmin->getConfigurationForPublisher($this->referenceName)->isAsyncPublishingEnabled(); + return $this->kafkaAdmin->getConfigurationForPublisher($this->referenceName)->isBatchPublishingEnabled(); + } + + public function isNonBlockingConfirmationEnabled(): bool + { + return $this->kafkaAdmin->getConfigurationForPublisher($this->referenceName)->isNonBlockingConfirmationEnabled(); } private function handleBatch(BatchMessage $batchMessage, Producer $producer, ProducerTopic $topic): void @@ -99,7 +104,7 @@ private function handleBatch(BatchMessage $batchMessage, Producer $producer, Pro throw $exception; } - if ($this->canPublishAsynchronously()) { + if ($this->canDeferConfirmation()) { $this->registerPendingDelivery($producer, $deliveryIds); return; @@ -130,7 +135,7 @@ private function produce(Message $message, ProducerTopic $topic, bool $trackDeli : null; try { - $retryDeadline = microtime(true) + ($this->kafkaAdmin->getConfigurationForPublisher($this->referenceName)->getAsyncPublishingTimeout() / 1000); + $retryDeadline = microtime(true) + ($this->kafkaAdmin->getConfigurationForPublisher($this->referenceName)->getConfirmationTimeout() / 1000); while (true) { try { $this->produceTracked($topic, $outboundMessage, $partitionKey, $headers, $deliveryId); @@ -173,9 +178,9 @@ private function produceTracked(ProducerTopic $topic, \Ecotone\Messaging\Channel ); } - private function canPublishAsynchronously(): bool + private function canDeferConfirmation(): bool { - return $this->isAsyncPublishingEnabled() && $this->asyncPublishingRegistry->isScopeActive(); + return $this->isNonBlockingConfirmationEnabled() && $this->pendingDeliveryRegistry->isScopeActive(); } /** @@ -183,13 +188,13 @@ private function canPublishAsynchronously(): bool */ private function registerPendingDelivery(Producer $producer, array $deliveryIds): void { - $this->asyncPublishingRegistry->register( + $this->pendingDeliveryRegistry->register( $this->referenceName, new KafkaPendingDelivery( $producer, $this->kafkaAdmin->getDeliveryTracker($this->referenceName), $deliveryIds, - $this->kafkaAdmin->getConfigurationForPublisher($this->referenceName)->getAsyncPublishingTimeout(), + $this->kafkaAdmin->getConfigurationForPublisher($this->referenceName)->getConfirmationTimeout(), $this->referenceName, ), ); @@ -209,7 +214,7 @@ private function flushSynchronously(Producer $producer, array $deliveryIds = []) if ($deliveryIds !== []) { $deliveryResult = $this->kafkaAdmin->getDeliveryTracker($this->referenceName)->collectResult($deliveryIds, $this->referenceName); if (! $deliveryResult->isSuccessful()) { - if ($this->isAsyncPublishingEnabled()) { + if ($this->isNonBlockingConfirmationEnabled()) { throw PublishingFailedException::withFailedDeliveries($deliveryResult->getFailedDeliveries()); } diff --git a/packages/Kafka/src/Outbound/KafkaOutboundChannelAdapterBuilder.php b/packages/Kafka/src/Outbound/KafkaOutboundChannelAdapterBuilder.php index 1c0497bae..8ec563a2d 100644 --- a/packages/Kafka/src/Outbound/KafkaOutboundChannelAdapterBuilder.php +++ b/packages/Kafka/src/Outbound/KafkaOutboundChannelAdapterBuilder.php @@ -5,7 +5,7 @@ namespace Ecotone\Kafka\Outbound; use Ecotone\Kafka\Configuration\KafkaAdmin; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDeliveryRegistry; use Ecotone\Messaging\Channel\PollableChannel\Serialization\OutboundMessageConverter; use Ecotone\Messaging\Config\Container\Definition; use Ecotone\Messaging\Config\Container\MessagingContainerBuilder; @@ -100,7 +100,7 @@ public function compile(MessagingContainerBuilder $builder): Definition new Reference(KafkaAdmin::class), new Reference(ConversionService::REFERENCE_NAME), $outboundMessageConverter, - new Reference(AsyncPublishingRegistry::class), + new Reference(PendingDeliveryRegistry::class), ]); } diff --git a/packages/Kafka/src/Outbound/KafkaPendingDelivery.php b/packages/Kafka/src/Outbound/KafkaPendingDelivery.php index 3811e2da7..37921320f 100644 --- a/packages/Kafka/src/Outbound/KafkaPendingDelivery.php +++ b/packages/Kafka/src/Outbound/KafkaPendingDelivery.php @@ -4,8 +4,8 @@ namespace Ecotone\Kafka\Outbound; -use Ecotone\Messaging\Channel\AsyncPublishing\DeliveryResult; -use Ecotone\Messaging\Channel\AsyncPublishing\PendingDelivery; +use Ecotone\Messaging\Channel\DeliveryConfirmation\DeliveryResult; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDelivery; use RdKafka\Producer; /** diff --git a/packages/Kafka/tests/Integration/AsyncPublishingReliabilityTest.php b/packages/Kafka/tests/Integration/HighThroughputPublishingReliabilityTest.php similarity index 90% rename from packages/Kafka/tests/Integration/AsyncPublishingReliabilityTest.php rename to packages/Kafka/tests/Integration/HighThroughputPublishingReliabilityTest.php index c36069602..f090b0982 100644 --- a/packages/Kafka/tests/Integration/AsyncPublishingReliabilityTest.php +++ b/packages/Kafka/tests/Integration/HighThroughputPublishingReliabilityTest.php @@ -8,7 +8,7 @@ use Ecotone\Kafka\Configuration\KafkaPublisherConfiguration; use Ecotone\Kafka\Outbound\MessagePublishingException; use Ecotone\Lite\EcotoneLite; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Config\ModulePackageList; use Ecotone\Messaging\Config\ServiceConfiguration; use Ecotone\Messaging\MessagePublisher; @@ -23,7 +23,7 @@ * @internal */ #[RunTestsInSeparateProcesses] -final class AsyncPublishingReliabilityTest extends TestCase +final class HighThroughputPublishingReliabilityTest extends TestCase { public function test_broker_rejected_message_fails_synchronous_fallback_of_async_publisher(): void { @@ -38,7 +38,7 @@ public function test_broker_rejected_message_fails_async_publish_on_future_resol { $publisher = $this->bootstrapPublisher(); - $future = $publisher->asyncPublish(str_repeat('x', 2_000_000)); + $future = $publisher->publishDeferred(str_repeat('x', 2_000_000)); $this->expectException(PublishingFailedException::class); @@ -74,7 +74,7 @@ private function bootstrapPublisher(): MessagePublisher ->withSkippedModulePackageNames(ModulePackageList::allPackagesExcept([ModulePackageList::KAFKA_PACKAGE])) ->withExtensionObjects([ KafkaPublisherConfiguration::createWithDefaults(topicName: Uuid::v7()->toRfc4122()) - ->withAsyncPublishing(timeoutInMilliseconds: 10000) + ->withHighThroughputPublishing(confirmationTimeoutInMilliseconds: 10000) ->setConfiguration('message.max.bytes', '4000000'), ]), licenceKey: LicenceTesting::VALID_LICENCE, diff --git a/packages/Kafka/tests/Integration/AsyncPublishingTest.php b/packages/Kafka/tests/Integration/HighThroughputPublishingTest.php similarity index 91% rename from packages/Kafka/tests/Integration/AsyncPublishingTest.php rename to packages/Kafka/tests/Integration/HighThroughputPublishingTest.php index 40a65ea87..539c33215 100644 --- a/packages/Kafka/tests/Integration/AsyncPublishingTest.php +++ b/packages/Kafka/tests/Integration/HighThroughputPublishingTest.php @@ -14,7 +14,7 @@ use Ecotone\Messaging\Attribute\Asynchronous; use Ecotone\Messaging\Attribute\Parameter\Reference; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Config\ModulePackageList; use Ecotone\Messaging\Config\ServiceConfiguration; use Ecotone\Messaging\Endpoint\ExecutionPollingMetadata; @@ -36,9 +36,9 @@ * @internal */ #[RunTestsInSeparateProcesses] -final class AsyncPublishingTest extends TestCase +final class HighThroughputPublishingTest extends TestCase { - public function test_multiple_messages_published_asynchronously_from_command_handler_are_delivered(): void + public function test_multiple_messages_published_from_command_handler_are_delivered(): void { $channelName = 'async_orders'; $orderService = $this->createOrderService($channelName); @@ -61,7 +61,7 @@ public function test_failing_to_deliver_asynchronously_published_messages_throws $channelName, $orderService, KafkaBrokerConfiguration::createWithDefaults(['wronghost:9092']), - asyncPublishingTimeout: 500, + confirmationTimeout: 500, ); $this->expectException(PublishingFailedException::class); @@ -69,7 +69,7 @@ public function test_failing_to_deliver_asynchronously_published_messages_throws $messaging->sendCommandWithRoutingKey('order.place', 'espresso'); } - public function test_async_publishing_requires_enterprise_licence(): void + public function test_high_throughput_publishing_requires_enterprise_licence(): void { $this->expectException(LicensingException::class); @@ -80,7 +80,7 @@ public function test_async_publishing_requires_enterprise_licence(): void ->withSkippedModulePackageNames(ModulePackageList::allPackagesExcept([ModulePackageList::KAFKA_PACKAGE])) ->withExtensionObjects([ KafkaPublisherConfiguration::createWithDefaults(topicName: Uuid::v7()->toRfc4122()) - ->withAsyncPublishing(), + ->withHighThroughputPublishing(), ]), ); } @@ -104,7 +104,7 @@ public function test_async_publishing_via_message_channel_requires_enterprise_li ); } - public function test_message_publisher_async_publish_confirms_delivery_on_future_resolve(): void + public function test_publish_deferred_confirms_delivery_on_future_resolve(): void { $messaging = EcotoneLite::bootstrapFlowTesting( [], @@ -113,14 +113,14 @@ public function test_message_publisher_async_publish_confirms_delivery_on_future ->withSkippedModulePackageNames(ModulePackageList::allPackagesExcept([ModulePackageList::KAFKA_PACKAGE])) ->withExtensionObjects([ KafkaPublisherConfiguration::createWithDefaults(topicName: Uuid::v7()->toRfc4122()) - ->withAsyncPublishing(), + ->withHighThroughputPublishing(), ]), licenceKey: LicenceTesting::VALID_LICENCE, ); $publisher = $messaging->getGateway(MessagePublisher::class); - $singleFuture = $publisher->asyncPublish('single order'); - $batchFuture = $publisher->asyncPublish( + $singleFuture = $publisher->publishDeferred('single order'); + $batchFuture = $publisher->publishDeferred( BatchMessage::constructEmpty() ->append('first order') ->append('second order', ['priority' => '5']) @@ -166,7 +166,7 @@ public function getReceived(): array ->withSkippedModulePackageNames(ModulePackageList::allPackagesExcept([ModulePackageList::ASYNCHRONOUS_PACKAGE, ModulePackageList::KAFKA_PACKAGE])) ->withExtensionObjects([ KafkaPublisherConfiguration::createWithDefaults(topicName: $topicName) - ->withAsyncPublishing(), + ->withHighThroughputPublishing(), TopicConfiguration::createWithReferenceName('batchOrdersTopic', $topicName), ]), licenceKey: LicenceTesting::VALID_LICENCE, @@ -214,7 +214,7 @@ public function getReceived(): array }; } - private function bootstrapEcotone(string $channelName, object $orderService, KafkaBrokerConfiguration $brokerConfiguration, ?int $asyncPublishingTimeout = null): FlowTestSupport + private function bootstrapEcotone(string $channelName, object $orderService, KafkaBrokerConfiguration $brokerConfiguration, ?int $confirmationTimeout = null): FlowTestSupport { $channelBuilder = KafkaMessageChannelBuilder::create( $channelName, @@ -222,8 +222,8 @@ private function bootstrapEcotone(string $channelName, object $orderService, Kaf messageGroupId: $uniqueId, )->withHighThroughputPublishing(); - if ($asyncPublishingTimeout !== null) { - $channelBuilder = $channelBuilder->withHighThroughputPublishing(timeoutInMilliseconds: $asyncPublishingTimeout); + if ($confirmationTimeout !== null) { + $channelBuilder = $channelBuilder->withHighThroughputPublishing(confirmationTimeoutInMilliseconds: $confirmationTimeout); } return EcotoneLite::bootstrapFlowTesting( diff --git a/packages/Redis/src/Configuration/RedisMessagePublisherConfiguration.php b/packages/Redis/src/Configuration/RedisMessagePublisherConfiguration.php index 4aaafbc3c..c165830fb 100644 --- a/packages/Redis/src/Configuration/RedisMessagePublisherConfiguration.php +++ b/packages/Redis/src/Configuration/RedisMessagePublisherConfiguration.php @@ -14,7 +14,7 @@ final class RedisMessagePublisherConfiguration { private bool $autoDeclareOnSend = true; private string $headerMapper = ''; - private bool $asyncPublishing = false; + private bool $batchPublishing = false; private function __construct(private string $connectionReference, private string $queueName, private ?string $outputDefaultConversionMediaType, private string $referenceName) { @@ -73,15 +73,19 @@ public function getReferenceName(): string return $this->referenceName; } - public function withAsyncPublishing(bool $asyncPublishing = true): self + /** + * Coalesces published Messages into a single scripted round trip. + * Non blocking confirmation is not offered here, as the round trip blocks until Redis confirms it. + */ + public function withHighThroughputPublishing(): self { - $this->asyncPublishing = $asyncPublishing; + $this->batchPublishing = true; return $this; } - public function isAsyncPublishingEnabled(): bool + public function isBatchPublishingEnabled(): bool { - return $this->asyncPublishing; + return $this->batchPublishing; } } diff --git a/packages/Redis/src/Configuration/RedisMessagePublisherModule.php b/packages/Redis/src/Configuration/RedisMessagePublisherModule.php index 82435d4af..cbe12b7b8 100644 --- a/packages/Redis/src/Configuration/RedisMessagePublisherModule.php +++ b/packages/Redis/src/Configuration/RedisMessagePublisherModule.php @@ -6,7 +6,7 @@ use Ecotone\AnnotationFinder\AnnotationFinder; use Ecotone\Messaging\Attribute\ModuleAnnotation; -use Ecotone\Messaging\Channel\AsyncPublishing\Config\AsyncPublishGatewayRegistration; +use Ecotone\Messaging\Channel\DeliveryConfirmation\Config\DeferredPublishingGatewayRegistration; use Ecotone\Messaging\Config\Annotation\AnnotationModule; use Ecotone\Messaging\Config\Annotation\ModuleConfiguration\ExtensionObjectResolver; use Ecotone\Messaging\Config\Annotation\ModuleConfiguration\NoExternalConfigurationModule; @@ -82,10 +82,10 @@ public function prepare(Configuration $messagingConfiguration, array $extensionO ->withAutoDeclareOnSend($messagePublisher->isAutoDeclareOnSend()) ->withHeaderMapper($messagePublisher->getHeaderMapper()) ->withDefaultConversionMediaType($mediaType) - ->withAsyncPublishing($messagePublisher->isAsyncPublishingEnabled()) + ->withBatchPublishing($messagePublisher->isBatchPublishingEnabled()) ); - AsyncPublishGatewayRegistration::registerFor($messagingConfiguration, $messagePublisher->getReferenceName(), $messagePublisher->isAsyncPublishingEnabled()); + DeferredPublishingGatewayRegistration::registerFor($messagingConfiguration, $messagePublisher->getReferenceName(), nonBlockingConfirmationEnabled: false); } } diff --git a/packages/Redis/src/RedisBackedMessageChannelBuilder.php b/packages/Redis/src/RedisBackedMessageChannelBuilder.php index c0096ac6b..08747f00a 100644 --- a/packages/Redis/src/RedisBackedMessageChannelBuilder.php +++ b/packages/Redis/src/RedisBackedMessageChannelBuilder.php @@ -33,16 +33,20 @@ public static function create(string $channelName, string $connectionReferenceNa return new self($channelName, $connectionReferenceName); } - public function withHighThroughputPublishing(bool $enabled = true): self + /** + * Coalesces published Messages into a single scripted round trip. + * Non blocking confirmation is not offered here, as the round trip blocks until Redis confirms it. + */ + public function withHighThroughputPublishing(): self { - $this->getRedisOutboundChannelAdapter()->withAsyncPublishing($enabled); + $this->getRedisOutboundChannelAdapter()->withBatchPublishing(); return $this; } protected function supportsBatchMessages(): bool { - return $this->getRedisOutboundChannelAdapter()->isAsyncPublishingEnabled(); + return $this->getRedisOutboundChannelAdapter()->isBatchPublishingEnabled(); } private function getRedisOutboundChannelAdapter(): RedisOutboundChannelAdapterBuilder diff --git a/packages/Redis/src/RedisOutboundChannelAdapter.php b/packages/Redis/src/RedisOutboundChannelAdapter.php index a115f6921..f85f310ed 100644 --- a/packages/Redis/src/RedisOutboundChannelAdapter.php +++ b/packages/Redis/src/RedisOutboundChannelAdapter.php @@ -7,9 +7,8 @@ use Ecotone\Enqueue\CachedConnectionFactory; use Ecotone\Enqueue\EnqueueOutboundChannelAdapter; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; -use Ecotone\Messaging\Channel\AsyncPublishing\FailedDelivery; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; +use Ecotone\Messaging\Channel\DeliveryConfirmation\FailedDelivery; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Channel\PollableChannel\Serialization\OutboundMessageConverter; use Ecotone\Messaging\Conversion\ConversionService; use Ecotone\Messaging\Message; @@ -54,8 +53,7 @@ public function __construct( bool $autoDeclare, OutboundMessageConverter $outboundMessageConverter, ConversionService $conversionService, - AsyncPublishingRegistry $asyncPublishingRegistry, - bool $asyncPublishing = false, + bool $batchPublishing = false, ) { parent::__construct( $connectionFactory, @@ -63,8 +61,7 @@ public function __construct( $autoDeclare, $outboundMessageConverter, $conversionService, - $asyncPublishingRegistry, - $asyncPublishing, + $batchPublishing, $queueName, ); } diff --git a/packages/Redis/src/RedisOutboundChannelAdapterBuilder.php b/packages/Redis/src/RedisOutboundChannelAdapterBuilder.php index 22311888f..97073fa0b 100644 --- a/packages/Redis/src/RedisOutboundChannelAdapterBuilder.php +++ b/packages/Redis/src/RedisOutboundChannelAdapterBuilder.php @@ -7,7 +7,6 @@ use Ecotone\Enqueue\CachedConnectionFactory; use Ecotone\Enqueue\EnqueueOutboundChannelAdapterBuilder; use Ecotone\Enqueue\HttpReconnectableConnectionFactory; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; use Ecotone\Messaging\Channel\PollableChannel\Serialization\OutboundMessageConverter; use Ecotone\Messaging\Config\Container\Definition; use Ecotone\Messaging\Config\Container\MessagingContainerBuilder; @@ -21,7 +20,7 @@ */ final class RedisOutboundChannelAdapterBuilder extends EnqueueOutboundChannelAdapterBuilder { - private bool $asyncPublishing = false; + private bool $batchPublishing = false; private function __construct(private string $queueName, private string $connectionFactoryReferenceName) { @@ -36,22 +35,22 @@ public static function createWith(string $queueName, string $connectionFactoryRe ); } - public function withAsyncPublishing(bool $asyncPublishing = true): self + public function withBatchPublishing(bool $batchPublishing = true): self { - $this->asyncPublishing = $asyncPublishing; + $this->batchPublishing = $batchPublishing; return $this; } - public function isAsyncPublishingEnabled(): bool + public function isBatchPublishingEnabled(): bool { - return $this->asyncPublishing; + return $this->batchPublishing; } public function compile(MessagingContainerBuilder $builder): Definition { - if ($this->asyncPublishing && ! $builder->getServiceConfiguration()->isRunningForEnterprise()) { - throw LicensingException::create('Asynchronous publishing is available only with Ecotone Enterprise licence.'); + if ($this->batchPublishing && ! $builder->getServiceConfiguration()->isRunningForEnterprise()) { + throw LicensingException::create('High Throughput Publishing is available only with Ecotone Enterprise licence.'); } $connectionFactory = new Definition(CachedConnectionFactory::class, [ @@ -75,8 +74,7 @@ public function compile(MessagingContainerBuilder $builder): Definition $this->autoDeclare, $outboundMessageConverter, new Reference(ConversionService::REFERENCE_NAME), - new Reference(AsyncPublishingRegistry::class), - $this->asyncPublishing, + $this->batchPublishing, ]); } } diff --git a/packages/Sqs/tests/Fixture/AsyncPublishing/OrderWasPlaced.php b/packages/Redis/tests/Fixture/HighThroughputPublishing/OrderWasPlaced.php similarity index 71% rename from packages/Sqs/tests/Fixture/AsyncPublishing/OrderWasPlaced.php rename to packages/Redis/tests/Fixture/HighThroughputPublishing/OrderWasPlaced.php index 1cf3396e4..bdb3ac8d6 100644 --- a/packages/Sqs/tests/Fixture/AsyncPublishing/OrderWasPlaced.php +++ b/packages/Redis/tests/Fixture/HighThroughputPublishing/OrderWasPlaced.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Test\Ecotone\Sqs\Fixture\AsyncPublishing; +namespace Test\Ecotone\Redis\Fixture\HighThroughputPublishing; /** * licence Apache-2.0 diff --git a/packages/Redis/tests/Integration/AsyncPublishingReliabilityTest.php b/packages/Redis/tests/Integration/HighThroughputPublishingReliabilityTest.php similarity index 95% rename from packages/Redis/tests/Integration/AsyncPublishingReliabilityTest.php rename to packages/Redis/tests/Integration/HighThroughputPublishingReliabilityTest.php index 181039554..6a8180c72 100644 --- a/packages/Redis/tests/Integration/AsyncPublishingReliabilityTest.php +++ b/packages/Redis/tests/Integration/HighThroughputPublishingReliabilityTest.php @@ -7,7 +7,7 @@ use Ecotone\Lite\EcotoneLite; use Ecotone\Lite\Test\FlowTestSupport; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Channel\PollableChannel\PollableChannelConfiguration; use Ecotone\Messaging\Config\ModulePackageList; use Ecotone\Messaging\Config\ServiceConfiguration; @@ -25,7 +25,7 @@ * licence Apache-2.0 * @internal */ -final class AsyncPublishingReliabilityTest extends ConnectionTestCase +final class HighThroughputPublishingReliabilityTest extends ConnectionTestCase { private const CHANNEL_NAME = 'partiallyFailingChannel'; diff --git a/packages/Redis/tests/Integration/AsyncPublishingTest.php b/packages/Redis/tests/Integration/HighThroughputPublishingTest.php similarity index 83% rename from packages/Redis/tests/Integration/AsyncPublishingTest.php rename to packages/Redis/tests/Integration/HighThroughputPublishingTest.php index 0116072ca..3624b637d 100644 --- a/packages/Redis/tests/Integration/AsyncPublishingTest.php +++ b/packages/Redis/tests/Integration/HighThroughputPublishingTest.php @@ -9,7 +9,7 @@ use Ecotone\Messaging\Attribute\Asynchronous; use Ecotone\Messaging\Attribute\Parameter\Reference; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Config\ModulePackageList; use Ecotone\Messaging\Config\ServiceConfiguration; use Ecotone\Messaging\Endpoint\ExecutionPollingMetadata; @@ -27,14 +27,14 @@ use Enqueue\Redis\RedisContext; use Symfony\Component\Uid\Uuid; use Test\Ecotone\Redis\ConnectionTestCase; -use Test\Ecotone\Redis\Fixture\AsyncPublishing\OrderWasPlaced; +use Test\Ecotone\Redis\Fixture\HighThroughputPublishing\OrderWasPlaced; use Throwable; /** * licence Apache-2.0 * @internal */ -final class AsyncPublishingTest extends ConnectionTestCase +final class HighThroughputPublishingTest extends ConnectionTestCase { public function setUp(): void { @@ -44,7 +44,7 @@ public function setUp(): void $context->getRedis()->del('asyncOrdersChannel:delayed'); } - public function test_multiple_messages_published_asynchronously_from_command_handler_are_delivered(): void + public function test_multiple_messages_published_from_command_handler_are_delivered(): void { $orderService = $this->createOrderService(); $messaging = $this->bootstrapEcotoneWithChannel($orderService, LicenceTesting::VALID_LICENCE); @@ -61,7 +61,7 @@ public function test_multiple_messages_published_asynchronously_from_command_han ); } - public function test_async_publishing_requires_enterprise_licence(): void + public function test_high_throughput_publishing_requires_enterprise_licence(): void { $orderService = $this->createOrderService(); @@ -70,7 +70,7 @@ public function test_async_publishing_requires_enterprise_licence(): void $this->bootstrapEcotoneWithChannel($orderService, licenceKey: null); } - public function test_async_publishing_via_message_publisher_requires_enterprise_licence(): void + public function test_high_throughput_publishing_via_message_publisher_requires_enterprise_licence(): void { $this->expectException(LicensingException::class); @@ -81,44 +81,36 @@ public function test_async_publishing_via_message_publisher_requires_enterprise_ ->withSkippedModulePackageNames(ModulePackageList::allPackagesExcept([ModulePackageList::REDIS_PACKAGE])) ->withExtensionObjects([ RedisMessagePublisherConfiguration::create(queueName: Uuid::v7()->toRfc4122()) - ->withAsyncPublishing(), + ->withHighThroughputPublishing(), ]), ); } - public function test_async_publish_on_publisher_without_async_configuration_throws_before_publishing(): void + public function test_publish_deferred_is_not_available_as_confirmation_can_not_be_deferred(): void { $queueName = Uuid::v7()->toRfc4122(); - $messaging = $this->bootstrapPublisher($queueName, asyncPublishing: false); + $messaging = $this->bootstrapPublisher($queueName, highThroughputPublishing: true); $publisher = $messaging->getGateway(MessagePublisher::class); - $publishFailed = false; - try { - $publisher->asyncPublish('order that must not be published'); - } catch (PublishingFailedException) { - $publishFailed = true; - } + $this->expectException(PublishingFailedException::class); + $this->expectExceptionMessageMatches('/not configured for non blocking confirmation/'); - $this->assertTrue($publishFailed); - $this->assertNull($messaging->getMessageChannel($queueName)->receive()); + $publisher->publishDeferred('order that must not be published'); } - public function test_message_publisher_async_publish_confirms_delivery_on_future_resolve(): void + public function test_message_publisher_delivers_single_and_batched_messages(): void { $queueName = Uuid::v7()->toRfc4122(); - $messaging = $this->bootstrapPublisher($queueName, asyncPublishing: true); + $messaging = $this->bootstrapPublisher($queueName, highThroughputPublishing: true); $publisher = $messaging->getGateway(MessagePublisher::class); - $singleFuture = $publisher->asyncPublish('single order'); - $batchFuture = $publisher->asyncPublish( + $publisher->send('single order'); + $publisher->convertAndSend( BatchMessage::constructEmpty() ->append('first order') ->append('second order', ['priority' => '5']) ); - $this->assertNull($singleFuture->resolve()); - $this->assertNull($batchFuture->resolve()); - $receivedPayloads = []; while ($message = $messaging->getMessageChannel($queueName)->receive()) { $receivedPayloads[] = $message->getPayload(); @@ -148,7 +140,7 @@ public function handle(string $order, #[Reference(MessagePublisher::class)] Mess ->withSkippedModulePackageNames(ModulePackageList::allPackagesExcept([ModulePackageList::ASYNCHRONOUS_PACKAGE, ModulePackageList::REDIS_PACKAGE])) ->withExtensionObjects([ RedisMessagePublisherConfiguration::create(queueName: $queueName) - ->withAsyncPublishing(), + ->withHighThroughputPublishing(), RedisBackedMessageChannelBuilder::create($queueName), ]), licenceKey: LicenceTesting::VALID_LICENCE, @@ -167,14 +159,14 @@ public function handle(string $order, #[Reference(MessagePublisher::class)] Mess public function test_delayed_entry_of_published_batch_lands_in_delayed_set(): void { $queueName = Uuid::v7()->toRfc4122(); - $messaging = $this->bootstrapPublisher($queueName, asyncPublishing: true); + $messaging = $this->bootstrapPublisher($queueName, highThroughputPublishing: true); $publisher = $messaging->getGateway(MessagePublisher::class); - $publisher->asyncPublish( + $publisher->convertAndSend( BatchMessage::constructEmpty() ->append('immediate order') ->append('delayed order', [MessageHeaders::DELIVERY_DELAY => 60000]) - )->resolve(); + ); $receivedPayloads = []; while ($message = $messaging->getMessageChannel($queueName)->receive()) { @@ -190,14 +182,14 @@ public function test_delayed_entry_of_published_batch_lands_in_delayed_set(): vo public function test_expired_entry_of_published_batch_is_not_delivered(): void { $queueName = Uuid::v7()->toRfc4122(); - $messaging = $this->bootstrapPublisher($queueName, asyncPublishing: true); + $messaging = $this->bootstrapPublisher($queueName, highThroughputPublishing: true); $publisher = $messaging->getGateway(MessagePublisher::class); - $publisher->asyncPublish( + $publisher->convertAndSend( BatchMessage::constructEmpty() ->append('expiring order', [MessageHeaders::TIME_TO_LIVE => 1000]) ->append('kept order') - )->resolve(); + ); sleep(2); @@ -209,7 +201,7 @@ public function test_expired_entry_of_published_batch_is_not_delivered(): void public function test_publishing_to_wrong_type_key_throws(): void { $queueName = Uuid::v7()->toRfc4122(); - $messaging = $this->bootstrapPublisher($queueName, asyncPublishing: true); + $messaging = $this->bootstrapPublisher($queueName, highThroughputPublishing: true); /** @var RedisContext $context */ $context = $this->getConnectionFactory()->createContext(); $context->getRedis()->eval('redis.call("set", KEYS[1], "blocked") return 1', [$queueName]); @@ -217,7 +209,7 @@ public function test_publishing_to_wrong_type_key_throws(): void $publishFailed = false; try { - $publisher->asyncPublish( + $publisher->convertAndSend( BatchMessage::constructEmpty() ->append('first order') ->append('second order') @@ -232,7 +224,7 @@ public function test_publishing_to_wrong_type_key_throws(): void public function test_partially_applied_mixed_batch_throws_while_immediate_entries_remain(): void { $queueName = Uuid::v7()->toRfc4122(); - $messaging = $this->bootstrapPublisher($queueName, asyncPublishing: true); + $messaging = $this->bootstrapPublisher($queueName, highThroughputPublishing: true); /** @var RedisContext $context */ $context = $this->getConnectionFactory()->createContext(); $context->getRedis()->lpush($queueName . ':delayed', 'poison entry forcing wrong type on the delayed set'); @@ -240,7 +232,7 @@ public function test_partially_applied_mixed_batch_throws_while_immediate_entrie $publishFailed = false; try { - $publisher->asyncPublish( + $publisher->convertAndSend( BatchMessage::constructEmpty() ->append('immediate order') ->append('delayed order', [MessageHeaders::DELIVERY_DELAY => 60000]) @@ -299,11 +291,11 @@ private function bootstrapEcotoneWithChannel(object $orderService, ?string $lice ); } - private function bootstrapPublisher(string $queueName, bool $asyncPublishing): FlowTestSupport + private function bootstrapPublisher(string $queueName, bool $highThroughputPublishing): FlowTestSupport { $publisherConfiguration = RedisMessagePublisherConfiguration::create(queueName: $queueName); - if ($asyncPublishing) { - $publisherConfiguration = $publisherConfiguration->withAsyncPublishing(); + if ($highThroughputPublishing) { + $publisherConfiguration = $publisherConfiguration->withHighThroughputPublishing(); } return EcotoneLite::bootstrapFlowTesting( diff --git a/packages/Sqs/src/Configuration/SqsMessagePublisherConfiguration.php b/packages/Sqs/src/Configuration/SqsMessagePublisherConfiguration.php index f38f4a1e3..ad5b573d1 100644 --- a/packages/Sqs/src/Configuration/SqsMessagePublisherConfiguration.php +++ b/packages/Sqs/src/Configuration/SqsMessagePublisherConfiguration.php @@ -15,8 +15,9 @@ final class SqsMessagePublisherConfiguration { private bool $autoDeclareOnSend = true; private string $headerMapper = ''; - private bool $asyncPublishing = false; - private ?int $asyncPublishingTimeout = null; + private bool $batchPublishing = false; + private bool $nonBlockingConfirmation = false; + private ?int $confirmationTimeout = null; private function __construct(private string $connectionReference, private string $queueName, private ?string $outputDefaultConversionMediaType, private string $referenceName) { @@ -75,24 +76,35 @@ public function getReferenceName(): string return $this->referenceName; } - public function withAsyncPublishing(bool $asyncPublishing = true, ?int $timeoutInMilliseconds = null): self + /** + * @param bool $batchPublishing coalesces published Messages into SQS batch send requests + * @param bool $nonBlockingConfirmation dispatches send requests without waiting on their responses, which are awaited on Future::resolve() or before the surrounding Command Bus or asynchronous endpoint finishes + * @param int|null $confirmationTimeoutInMilliseconds how long to await a send response before treating the delivery as failed + */ + public function withHighThroughputPublishing(bool $batchPublishing = true, bool $nonBlockingConfirmation = true, ?int $confirmationTimeoutInMilliseconds = null): self { - Assert::isTrue($timeoutInMilliseconds === null || $timeoutInMilliseconds > 0, 'Async publishing timeout must be a positive amount of milliseconds.'); - $this->asyncPublishing = $asyncPublishing; - if ($timeoutInMilliseconds !== null) { - $this->asyncPublishingTimeout = $timeoutInMilliseconds; + Assert::isTrue($confirmationTimeoutInMilliseconds === null || $confirmationTimeoutInMilliseconds > 0, 'Confirmation timeout must be a positive amount of milliseconds.'); + $this->batchPublishing = $batchPublishing; + $this->nonBlockingConfirmation = $nonBlockingConfirmation; + if ($confirmationTimeoutInMilliseconds !== null) { + $this->confirmationTimeout = $confirmationTimeoutInMilliseconds; } return $this; } - public function isAsyncPublishingEnabled(): bool + public function isBatchPublishingEnabled(): bool + { + return $this->batchPublishing; + } + + public function isNonBlockingConfirmationEnabled(): bool { - return $this->asyncPublishing; + return $this->nonBlockingConfirmation; } - public function getAsyncPublishingTimeout(): ?int + public function getConfirmationTimeout(): ?int { - return $this->asyncPublishingTimeout; + return $this->confirmationTimeout; } } diff --git a/packages/Sqs/src/Configuration/SqsMessagePublisherModule.php b/packages/Sqs/src/Configuration/SqsMessagePublisherModule.php index b65b234ad..970752be9 100644 --- a/packages/Sqs/src/Configuration/SqsMessagePublisherModule.php +++ b/packages/Sqs/src/Configuration/SqsMessagePublisherModule.php @@ -6,7 +6,7 @@ use Ecotone\AnnotationFinder\AnnotationFinder; use Ecotone\Messaging\Attribute\ModuleAnnotation; -use Ecotone\Messaging\Channel\AsyncPublishing\Config\AsyncPublishGatewayRegistration; +use Ecotone\Messaging\Channel\DeliveryConfirmation\Config\DeferredPublishingGatewayRegistration; use Ecotone\Messaging\Config\Annotation\AnnotationModule; use Ecotone\Messaging\Config\Annotation\ModuleConfiguration\ExtensionObjectResolver; use Ecotone\Messaging\Config\Annotation\ModuleConfiguration\NoExternalConfigurationModule; @@ -82,10 +82,10 @@ public function prepare(Configuration $messagingConfiguration, array $extensionO ->withAutoDeclareOnSend($messagePublisher->isAutoDeclareOnSend()) ->withHeaderMapper($messagePublisher->getHeaderMapper()) ->withDefaultConversionMediaType($mediaType) - ->withAsyncPublishing($messagePublisher->isAsyncPublishingEnabled(), $messagePublisher->getAsyncPublishingTimeout()) + ->withHighThroughputPublishing($messagePublisher->isBatchPublishingEnabled(), $messagePublisher->isNonBlockingConfirmationEnabled(), $messagePublisher->getConfirmationTimeout()) ); - AsyncPublishGatewayRegistration::registerFor($messagingConfiguration, $messagePublisher->getReferenceName(), $messagePublisher->isAsyncPublishingEnabled()); + DeferredPublishingGatewayRegistration::registerFor($messagingConfiguration, $messagePublisher->getReferenceName(), $messagePublisher->isNonBlockingConfirmationEnabled()); } } diff --git a/packages/Sqs/src/SqsBackedMessageChannelBuilder.php b/packages/Sqs/src/SqsBackedMessageChannelBuilder.php index cb6df98e4..ca455c567 100644 --- a/packages/Sqs/src/SqsBackedMessageChannelBuilder.php +++ b/packages/Sqs/src/SqsBackedMessageChannelBuilder.php @@ -33,16 +33,21 @@ public static function create(string $channelName, string $connectionReferenceNa return new self($channelName, $connectionReferenceName); } - public function withHighThroughputPublishing(bool $enabled = true, ?int $timeoutInMilliseconds = null): self + /** + * @param bool $batchPublishing coalesces published Messages into SQS batch send requests + * @param bool $nonBlockingConfirmation dispatches send requests without waiting on their responses, which are awaited before the surrounding Command Bus or asynchronous endpoint finishes + * @param int|null $confirmationTimeoutInMilliseconds how long to await a send response before treating the delivery as failed + */ + public function withHighThroughputPublishing(bool $batchPublishing = true, bool $nonBlockingConfirmation = true, ?int $confirmationTimeoutInMilliseconds = null): self { - $this->getSqsOutboundChannelAdapter()->withAsyncPublishing($enabled, $timeoutInMilliseconds); + $this->getSqsOutboundChannelAdapter()->withHighThroughputPublishing($batchPublishing, $nonBlockingConfirmation, $confirmationTimeoutInMilliseconds); return $this; } protected function supportsBatchMessages(): bool { - return $this->getSqsOutboundChannelAdapter()->isAsyncPublishingEnabled(); + return $this->getSqsOutboundChannelAdapter()->isBatchPublishingEnabled(); } private function getSqsOutboundChannelAdapter(): SqsOutboundChannelAdapterBuilder diff --git a/packages/Sqs/src/SqsOutboundChannelAdapter.php b/packages/Sqs/src/SqsOutboundChannelAdapter.php index 4cd75e82c..969ad1f72 100644 --- a/packages/Sqs/src/SqsOutboundChannelAdapter.php +++ b/packages/Sqs/src/SqsOutboundChannelAdapter.php @@ -7,8 +7,8 @@ use Ecotone\Enqueue\CachedConnectionFactory; use Ecotone\Enqueue\EnqueueOutboundChannelAdapter; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDeliveryRegistry; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Channel\PollableChannel\Serialization\OutboundMessageConverter; use Ecotone\Messaging\Config\ConfigurationException; use Ecotone\Messaging\Conversion\ConversionService; @@ -34,9 +34,10 @@ public function __construct( bool $autoDeclare, OutboundMessageConverter $outboundMessageConverter, ConversionService $conversionService, - private AsyncPublishingRegistry $asyncPublishingRegistry, - private bool $asyncPublishing = false, - private int $asyncPublishingTimeout = SqsOutboundChannelAdapterBuilder::DEFAULT_ASYNC_PUBLISHING_TIMEOUT, + private PendingDeliveryRegistry $pendingDeliveryRegistry, + private bool $batchPublishing = false, + private bool $nonBlockingConfirmation = false, + private int $confirmationTimeout = SqsOutboundChannelAdapterBuilder::DEFAULT_CONFIRMATION_TIMEOUT, ) { $this->requestDispatchPool = new SqsRequestDispatchPool(); parent::__construct( @@ -45,8 +46,7 @@ public function __construct( $autoDeclare, $outboundMessageConverter, $conversionService, - $asyncPublishingRegistry, - $asyncPublishing, + $batchPublishing, $queueName, ); } @@ -61,8 +61,8 @@ public function initialize(): void public function handle(Message $message): void { - if ($message->getPayload() instanceof BatchMessage && ! $this->asyncPublishing) { - throw ConfigurationException::create(sprintf('Sending BatchMessage over `%s` requires async publishing to be enabled. Enable it with withAsyncPublishing(), available as part of Ecotone Enterprise.', $this->queueName)); + if ($message->getPayload() instanceof BatchMessage && ! $this->batchPublishing) { + throw ConfigurationException::create(sprintf('Sending BatchMessage over `%s` requires batch publishing to be enabled. Enable it with withHighThroughputPublishing(), available as part of Ecotone Enterprise.', $this->queueName)); } /** @var SqsContext $context */ @@ -88,8 +88,8 @@ public function handle(Message $message): void $pendingDelivery = new SqsPendingDelivery($sendRequestPromises, $trackedMessagesPerRequest, $this->queueName); - if ($this->asyncPublishing && $this->asyncPublishingRegistry->isScopeActive()) { - $this->asyncPublishingRegistry->register($this->queueName, $pendingDelivery); + if ($this->nonBlockingConfirmation && $this->pendingDeliveryRegistry->isScopeActive()) { + $this->pendingDeliveryRegistry->register($this->queueName, $pendingDelivery); return; } @@ -181,7 +181,7 @@ private function buildBatchRequest(SqsDestination $destination, string $queueUrl 'Entries' => $entries, ]; - $arguments['@http'] = ['timeout' => $this->asyncPublishingTimeout / 1000]; + $arguments['@http'] = ['timeout' => $this->confirmationTimeout / 1000]; return ['arguments' => $arguments, 'trackedMessages' => $trackedMessages]; } diff --git a/packages/Sqs/src/SqsOutboundChannelAdapterBuilder.php b/packages/Sqs/src/SqsOutboundChannelAdapterBuilder.php index d3b1cbe42..24084eab3 100644 --- a/packages/Sqs/src/SqsOutboundChannelAdapterBuilder.php +++ b/packages/Sqs/src/SqsOutboundChannelAdapterBuilder.php @@ -7,7 +7,7 @@ use Ecotone\Enqueue\CachedConnectionFactory; use Ecotone\Enqueue\EnqueueOutboundChannelAdapterBuilder; use Ecotone\Enqueue\HttpReconnectableConnectionFactory; -use Ecotone\Messaging\Channel\AsyncPublishing\AsyncPublishingRegistry; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDeliveryRegistry; use Ecotone\Messaging\Channel\PollableChannel\Serialization\OutboundMessageConverter; use Ecotone\Messaging\Config\Container\Definition; use Ecotone\Messaging\Config\Container\MessagingContainerBuilder; @@ -22,10 +22,11 @@ */ final class SqsOutboundChannelAdapterBuilder extends EnqueueOutboundChannelAdapterBuilder { - public const DEFAULT_ASYNC_PUBLISHING_TIMEOUT = 25000; + public const DEFAULT_CONFIRMATION_TIMEOUT = 25000; - private bool $asyncPublishing = false; - private int $asyncPublishingTimeout = self::DEFAULT_ASYNC_PUBLISHING_TIMEOUT; + private bool $batchPublishing = false; + private bool $nonBlockingConfirmation = false; + private int $confirmationTimeout = self::DEFAULT_CONFIRMATION_TIMEOUT; private function __construct(private string $queueName, private string $connectionFactoryReferenceName) { @@ -37,26 +38,32 @@ public static function create(string $queueName, string $connectionFactoryRefere return new self($queueName, $connectionFactoryReferenceName); } - public function withAsyncPublishing(bool $asyncPublishing = true, ?int $timeoutInMilliseconds = null): self + public function withHighThroughputPublishing(bool $batchPublishing = true, bool $nonBlockingConfirmation = true, ?int $confirmationTimeoutInMilliseconds = null): self { - Assert::isTrue($timeoutInMilliseconds === null || $timeoutInMilliseconds > 0, 'Async publishing timeout must be a positive amount of milliseconds.'); - $this->asyncPublishing = $asyncPublishing; - if ($timeoutInMilliseconds !== null) { - $this->asyncPublishingTimeout = $timeoutInMilliseconds; + Assert::isTrue($confirmationTimeoutInMilliseconds === null || $confirmationTimeoutInMilliseconds > 0, 'Confirmation timeout must be a positive amount of milliseconds.'); + $this->batchPublishing = $batchPublishing; + $this->nonBlockingConfirmation = $nonBlockingConfirmation; + if ($confirmationTimeoutInMilliseconds !== null) { + $this->confirmationTimeout = $confirmationTimeoutInMilliseconds; } return $this; } - public function isAsyncPublishingEnabled(): bool + public function isBatchPublishingEnabled(): bool { - return $this->asyncPublishing; + return $this->batchPublishing; + } + + public function isNonBlockingConfirmationEnabled(): bool + { + return $this->nonBlockingConfirmation; } public function compile(MessagingContainerBuilder $builder): Definition { - if ($this->asyncPublishing && ! $builder->getServiceConfiguration()->isRunningForEnterprise()) { - throw LicensingException::create('Asynchronous publishing is available only with Ecotone Enterprise licence.'); + if (($this->batchPublishing || $this->nonBlockingConfirmation) && ! $builder->getServiceConfiguration()->isRunningForEnterprise()) { + throw LicensingException::create('High Throughput Publishing is available only with Ecotone Enterprise licence.'); } $connectionFactory = new Definition(CachedConnectionFactory::class, [ @@ -80,9 +87,10 @@ public function compile(MessagingContainerBuilder $builder): Definition $this->autoDeclare, $outboundMessageConverter, new Reference(ConversionService::REFERENCE_NAME), - new Reference(AsyncPublishingRegistry::class), - $this->asyncPublishing, - $this->asyncPublishingTimeout, + new Reference(PendingDeliveryRegistry::class), + $this->batchPublishing, + $this->nonBlockingConfirmation, + $this->confirmationTimeout, ]); } } diff --git a/packages/Sqs/src/SqsPendingDelivery.php b/packages/Sqs/src/SqsPendingDelivery.php index 5d1e3ca6e..09535c609 100644 --- a/packages/Sqs/src/SqsPendingDelivery.php +++ b/packages/Sqs/src/SqsPendingDelivery.php @@ -5,9 +5,9 @@ namespace Ecotone\Sqs; use Aws\Result; -use Ecotone\Messaging\Channel\AsyncPublishing\DeliveryResult; -use Ecotone\Messaging\Channel\AsyncPublishing\FailedDelivery; -use Ecotone\Messaging\Channel\AsyncPublishing\PendingDelivery; +use Ecotone\Messaging\Channel\DeliveryConfirmation\DeliveryResult; +use Ecotone\Messaging\Channel\DeliveryConfirmation\FailedDelivery; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PendingDelivery; use Ecotone\Messaging\Message; use GuzzleHttp\Promise\PromiseInterface; use GuzzleHttp\Promise\Utils; diff --git a/packages/Dbal/tests/Fixture/AsyncPublishing/OrderWasPlaced.php b/packages/Sqs/tests/Fixture/HighThroughputPublishing/OrderWasPlaced.php similarity index 72% rename from packages/Dbal/tests/Fixture/AsyncPublishing/OrderWasPlaced.php rename to packages/Sqs/tests/Fixture/HighThroughputPublishing/OrderWasPlaced.php index b1b0edf39..8bf2049e1 100644 --- a/packages/Dbal/tests/Fixture/AsyncPublishing/OrderWasPlaced.php +++ b/packages/Sqs/tests/Fixture/HighThroughputPublishing/OrderWasPlaced.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Test\Ecotone\Dbal\Fixture\AsyncPublishing; +namespace Test\Ecotone\Sqs\Fixture\HighThroughputPublishing; /** * licence Apache-2.0 diff --git a/packages/Sqs/tests/Integration/AsyncPublishingReliabilityTest.php b/packages/Sqs/tests/Integration/HighThroughputPublishingReliabilityTest.php similarity index 84% rename from packages/Sqs/tests/Integration/AsyncPublishingReliabilityTest.php rename to packages/Sqs/tests/Integration/HighThroughputPublishingReliabilityTest.php index ed9707cac..7bb0258ac 100644 --- a/packages/Sqs/tests/Integration/AsyncPublishingReliabilityTest.php +++ b/packages/Sqs/tests/Integration/HighThroughputPublishingReliabilityTest.php @@ -8,7 +8,7 @@ use Ecotone\Lite\Test\FlowTestSupport; use Ecotone\Messaging\Attribute\Parameter\Reference; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Config\ModulePackageList; use Ecotone\Messaging\Config\ServiceConfiguration; use Ecotone\Messaging\MessagePublisher; @@ -25,14 +25,14 @@ * licence Apache-2.0 * @internal */ -final class AsyncPublishingReliabilityTest extends ConnectionTestCase +final class HighThroughputPublishingReliabilityTest extends ConnectionTestCase { public function test_broker_rejected_batch_fails_async_publish_on_future_resolve(): void { $messaging = $this->bootstrapPublisher(Uuid::v7()->toRfc4122()); $publisher = $messaging->getGateway(MessagePublisher::class); - $future = $publisher->asyncPublish( + $future = $publisher->publishDeferred( BatchMessage::constructEmpty() ->append('valid order') ->append(str_repeat('x', 300_000)) @@ -63,9 +63,9 @@ public function test_one_failing_batch_among_many_published_in_command_handler_f #[CommandHandler('order.placeAllBatches')] public function handle(string $order, #[Reference(MessagePublisher::class)] MessagePublisher $publisher): void { - $publisher->asyncPublish(BatchMessage::constructEmpty()->append($order . ' first valid order')); - $publisher->asyncPublish(BatchMessage::constructEmpty()->append(str_repeat('x', 300_000))); - $publisher->asyncPublish(BatchMessage::constructEmpty()->append($order . ' third valid order')); + $publisher->publishDeferred(BatchMessage::constructEmpty()->append($order . ' first valid order')); + $publisher->publishDeferred(BatchMessage::constructEmpty()->append(str_repeat('x', 300_000))); + $publisher->publishDeferred(BatchMessage::constructEmpty()->append($order . ' third valid order')); } }; $messaging = EcotoneLite::bootstrapFlowTesting( @@ -75,7 +75,7 @@ public function handle(string $order, #[Reference(MessagePublisher::class)] Mess ->withSkippedModulePackageNames(ModulePackageList::allPackagesExcept([ModulePackageList::SQS_PACKAGE])) ->withExtensionObjects([ SqsMessagePublisherConfiguration::create(queueName: Uuid::v7()->toRfc4122()) - ->withAsyncPublishing(timeoutInMilliseconds: 10000), + ->withHighThroughputPublishing(confirmationTimeoutInMilliseconds: 10000), ]), licenceKey: LicenceTesting::VALID_LICENCE, ); @@ -94,7 +94,7 @@ private function bootstrapPublisher(string $queueName): FlowTestSupport ->withSkippedModulePackageNames(ModulePackageList::allPackagesExcept([ModulePackageList::SQS_PACKAGE])) ->withExtensionObjects([ SqsMessagePublisherConfiguration::create(queueName: $queueName) - ->withAsyncPublishing(timeoutInMilliseconds: 10000), + ->withHighThroughputPublishing(confirmationTimeoutInMilliseconds: 10000), ]), licenceKey: LicenceTesting::VALID_LICENCE, ); diff --git a/packages/Sqs/tests/Integration/AsyncPublishingTest.php b/packages/Sqs/tests/Integration/HighThroughputPublishingTest.php similarity index 85% rename from packages/Sqs/tests/Integration/AsyncPublishingTest.php rename to packages/Sqs/tests/Integration/HighThroughputPublishingTest.php index 492c746e1..ae2a13221 100644 --- a/packages/Sqs/tests/Integration/AsyncPublishingTest.php +++ b/packages/Sqs/tests/Integration/HighThroughputPublishingTest.php @@ -9,7 +9,7 @@ use Ecotone\Messaging\Attribute\Asynchronous; use Ecotone\Messaging\Attribute\Parameter\Reference; use Ecotone\Messaging\BatchMessage; -use Ecotone\Messaging\Channel\AsyncPublishing\PublishingFailedException; +use Ecotone\Messaging\Channel\DeliveryConfirmation\PublishingFailedException; use Ecotone\Messaging\Config\ModulePackageList; use Ecotone\Messaging\Config\ServiceConfiguration; use Ecotone\Messaging\Endpoint\ExecutionPollingMetadata; @@ -26,15 +26,15 @@ use Enqueue\Sqs\SqsConnectionFactory; use Symfony\Component\Uid\Uuid; use Test\Ecotone\Sqs\ConnectionTestCase; -use Test\Ecotone\Sqs\Fixture\AsyncPublishing\OrderWasPlaced; +use Test\Ecotone\Sqs\Fixture\HighThroughputPublishing\OrderWasPlaced; /** * licence Apache-2.0 * @internal */ -final class AsyncPublishingTest extends ConnectionTestCase +final class HighThroughputPublishingTest extends ConnectionTestCase { - public function test_multiple_messages_published_asynchronously_from_command_handler_are_delivered(): void + public function test_multiple_messages_published_from_command_handler_are_delivered(): void { $orderService = $this->createOrderService(); $messaging = $this->bootstrapEcotoneWithChannel($orderService, LicenceTesting::VALID_LICENCE); @@ -50,7 +50,7 @@ public function test_multiple_messages_published_asynchronously_from_command_han $this->assertSame(['espresso-1', 'espresso-2', 'espresso-3'], $receivedEvents); } - public function test_async_publishing_requires_enterprise_licence(): void + public function test_high_throughput_publishing_requires_enterprise_licence(): void { $orderService = $this->createOrderService(); @@ -59,7 +59,7 @@ public function test_async_publishing_requires_enterprise_licence(): void $this->bootstrapEcotoneWithChannel($orderService, licenceKey: null); } - public function test_async_publishing_via_message_publisher_requires_enterprise_licence(): void + public function test_high_throughput_publishing_via_message_publisher_requires_enterprise_licence(): void { $this->expectException(LicensingException::class); @@ -70,20 +70,20 @@ public function test_async_publishing_via_message_publisher_requires_enterprise_ ->withSkippedModulePackageNames(ModulePackageList::allPackagesExcept([ModulePackageList::SQS_PACKAGE])) ->withExtensionObjects([ SqsMessagePublisherConfiguration::create(queueName: Uuid::v7()->toRfc4122()) - ->withAsyncPublishing(), + ->withHighThroughputPublishing(), ]), ); } - public function test_async_publish_on_publisher_without_async_configuration_throws_before_publishing(): void + public function test_publish_deferred_on_publisher_without_non_blocking_confirmation_throws_before_publishing(): void { $queueName = Uuid::v7()->toRfc4122(); - $messaging = $this->bootstrapPublisher($queueName, asyncPublishing: false); + $messaging = $this->bootstrapPublisher($queueName, highThroughputPublishing: false); $publisher = $messaging->getGateway(MessagePublisher::class); $publishFailed = false; try { - $publisher->asyncPublish('order that must not be published'); + $publisher->publishDeferred('order that must not be published'); } catch (PublishingFailedException) { $publishFailed = true; } @@ -92,14 +92,14 @@ public function test_async_publish_on_publisher_without_async_configuration_thro $this->assertNull($messaging->getMessageChannel($queueName)->receive()); } - public function test_message_publisher_async_publish_confirms_delivery_on_future_resolve(): void + public function test_publish_deferred_confirms_delivery_on_future_resolve(): void { $queueName = Uuid::v7()->toRfc4122(); - $messaging = $this->bootstrapPublisher($queueName, asyncPublishing: true); + $messaging = $this->bootstrapPublisher($queueName, highThroughputPublishing: true); $publisher = $messaging->getGateway(MessagePublisher::class); - $singleFuture = $publisher->asyncPublish('single order'); - $batchFuture = $publisher->asyncPublish( + $singleFuture = $publisher->publishDeferred('single order'); + $batchFuture = $publisher->publishDeferred( BatchMessage::constructEmpty() ->append('first order') ->append('second order', ['priority' => '5']) @@ -137,7 +137,7 @@ public function handle(string $order, #[Reference(MessagePublisher::class)] Mess ->withSkippedModulePackageNames(ModulePackageList::allPackagesExcept([ModulePackageList::ASYNCHRONOUS_PACKAGE, ModulePackageList::SQS_PACKAGE])) ->withExtensionObjects([ SqsMessagePublisherConfiguration::create(queueName: $queueName) - ->withAsyncPublishing(timeoutInMilliseconds: 10000), + ->withHighThroughputPublishing(confirmationTimeoutInMilliseconds: 10000), SqsBackedMessageChannelBuilder::create($queueName), ]), licenceKey: LicenceTesting::VALID_LICENCE, @@ -156,7 +156,7 @@ public function handle(string $order, #[Reference(MessagePublisher::class)] Mess public function test_batch_larger_than_ten_messages_is_chunked_and_delivered(): void { $queueName = Uuid::v7()->toRfc4122(); - $messaging = $this->bootstrapPublisher($queueName, asyncPublishing: true); + $messaging = $this->bootstrapPublisher($queueName, highThroughputPublishing: true); $publisher = $messaging->getGateway(MessagePublisher::class); $batch = BatchMessage::constructEmpty(); @@ -164,7 +164,7 @@ public function test_batch_larger_than_ten_messages_is_chunked_and_delivered(): $batch = $batch->append('order ' . $orderNumber); } - $this->assertNull($publisher->asyncPublish($batch)->resolve()); + $this->assertNull($publisher->publishDeferred($batch)->resolve()); $receivedPayloads = []; while ($message = $messaging->getMessageChannel($queueName)->receive()) { @@ -176,11 +176,11 @@ public function test_batch_larger_than_ten_messages_is_chunked_and_delivered(): public function test_delayed_entry_of_published_batch_is_delivered_after_delay(): void { $queueName = Uuid::v7()->toRfc4122(); - $messaging = $this->bootstrapPublisher($queueName, asyncPublishing: true); + $messaging = $this->bootstrapPublisher($queueName, highThroughputPublishing: true); $publisher = $messaging->getGateway(MessagePublisher::class); $publishedAt = microtime(true); - $publisher->asyncPublish( + $publisher->publishDeferred( BatchMessage::constructEmpty() ->append('immediate order') ->append('delayed order', [MessageHeaders::DELIVERY_DELAY => 1000]) @@ -246,11 +246,11 @@ private function bootstrapEcotoneWithChannel(object $orderService, ?string $lice ); } - private function bootstrapPublisher(string $queueName, bool $asyncPublishing): FlowTestSupport + private function bootstrapPublisher(string $queueName, bool $highThroughputPublishing): FlowTestSupport { $publisherConfiguration = SqsMessagePublisherConfiguration::create(queueName: $queueName); - if ($asyncPublishing) { - $publisherConfiguration = $publisherConfiguration->withAsyncPublishing(); + if ($highThroughputPublishing) { + $publisherConfiguration = $publisherConfiguration->withHighThroughputPublishing(); } return EcotoneLite::bootstrapFlowTesting(