From d31c0aa3b70ddb3092592dc8c87cfa3e45ace26a Mon Sep 17 00:00:00 2001 From: Abdulsalam alblihi Date: Sat, 20 Apr 2024 16:32:46 +0300 Subject: [PATCH] Add support for laravel octane (#12) * Add support for laravel octane, by keeping the resolved Producer and Consumer service container will keep the connection active for all requests coming to the same worker * readme update * reverted the consumer connection property * indentation fix * testcase added for producer reusability --------- Co-authored-by: Syed Sirajul Islam Anik --- readme.md | 16 ++++++++++++++++ src/Amqp.php | 4 +++- tests/AmqpTest.php | 43 +++++++++++++++++++++++++++++++++++-------- 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/readme.md b/readme.md index e9eb3f2..f319a05 100644 --- a/readme.md +++ b/readme.md @@ -84,6 +84,21 @@ name. - `amqp.connections.*.consumer` holds the default properties of consumer when consuming. - `amqp.connections.*.qos` holds the default properties of QoS when consuming. +### Octane support + +This package supports laravel octane by +default. [To keep the AMQP connection alive](https://www.cloudamqp.com/blog/part4-rabbitmq-13-common-errors.html), +you have to configure octane to `warm` the connection, by adding **'amqp'** to the warm array in octane configurations. + +```php +// config/octane.php +// ... +'warm' => [ + // ... + 'amqp', // <-- this line +], +``` + ## Usage The followings work the same. @@ -274,6 +289,7 @@ class MyTest extends TestCase - If a message exactly matches the `$message`. - If a message exactly matches the `get_class($message)`. - If a message is an implementation of `$message`. + ### Note Using `Anik\Laravel\Amqp\Facades\Amqp::consume()` after `Anik\Laravel\Amqp\Facades\Amqp::fake()` will throw exception. diff --git a/src/Amqp.php b/src/Amqp.php index c49213e..0e17311 100644 --- a/src/Amqp.php +++ b/src/Amqp.php @@ -20,6 +20,8 @@ class Amqp implements AmqpPubSub private $config; + private Producer $producer; + public function __construct(AbstractConnection $connection, array $config = []) { $this->connection = $connection; @@ -28,7 +30,7 @@ public function __construct(AbstractConnection $connection, array $config = []) public function getProducer(): Producer { - return app()->make(Producer::class, ['connection' => $this->connection]); + return $this->producer ??= app()->make(Producer::class, ['connection' => $this->connection]); } public function getConsumer(array $options = []): Consumer diff --git a/tests/AmqpTest.php b/tests/AmqpTest.php index bd78958..167022e 100644 --- a/tests/AmqpTest.php +++ b/tests/AmqpTest.php @@ -347,7 +347,7 @@ public static function consumerConfigTestDataProvider(): array /** * @dataProvider publishMessageTestDataProvider * - * @param array $data + * @param array $data */ public function testPublishFormatsMessagesToProducible(array $data) { @@ -432,7 +432,7 @@ public function testPublishPassesExpectedRoutingKey() /** * @dataProvider publishOptionsDataProvider * - * @param array $data + * @param array $data */ public function testPublishPassesPublishOptionsIfAvailable(array $data) { @@ -471,7 +471,7 @@ function ($options) use ($expectations) { /** * @dataProvider exchangeTestDataProvider * - * @param array $data + * @param array $data */ public function testPublishMessageProcessesExchange(array $data) { @@ -511,6 +511,33 @@ function ($options) use ($expectation) { ); } + public function testReusesResolvedProducer() + { + $this->bindProducerToApp($producer = $this->getProducerMock($this->connection)); + + $producer->expects($this->exactly(2))->method('publishBatch'); + + $amqp = $this->getAmqpInstance(null, ['exchange' => ['name' => self::EXCHANGE_NAME, 'type' => 'topic']]); + $amqp->publish( + 'my message', + 'my-binding-key', + $data['exchange'] ?? null, + $data['options'] ?? [] + ); + + // Bind new producer to the container + $this->bindProducerToApp($producer2 = $this->getProducerMock($this->connection)); + // This producer should never be called as the previous producer is already stored in the class. + $producer2->expects($this->never())->method('publishBatch'); + + $amqp->publish( + 'my another message', + 'my-binding-key', + $data['exchange'] ?? null, + $data['options'] ?? [] + ); + } + public function testConsumeHandlerIsChangesCallableToConsumable() { $this->bindConsumerToApp($consumer = $this->getConsumerMock($this->connection)); @@ -563,7 +590,7 @@ function () { /** * @dataProvider exchangeTestDataProvider * - * @param array $data + * @param array $data */ public function testConsumerProcessesExchange(array $data) { @@ -611,7 +638,7 @@ function () { /** * @dataProvider queueTestDataProvider * - * @param array $data + * @param array $data */ public function testConsumerProcessesQueue(array $data) { @@ -659,7 +686,7 @@ function () { /** * @dataProvider qosTestDataProvider * - * @param array $data + * @param array $data */ public function testConsumerProcessesQos(array $data) { @@ -714,7 +741,7 @@ function () { /** * @dataProvider queueBindTestDataProvider * - * @param array $data + * @param array $data */ public function testConsumerProcessesQueueBind(array $data) { @@ -762,7 +789,7 @@ function () { /** * @dataProvider consumerConfigTestDataProvider * - * @param array $data + * @param array $data */ public function testConsumerProcessesConsumerConfig(array $data) {